Java muitos arquivos abertos

Eu estou tentando gravar em vários arquivos, 19 para ser exato. Depois de escrever para eles algumas centenas de vezes, recebo o Java IOException: muitos arquivos abertos. Mas, como eu disse, tenho exatamente 19 arquivos abertos e abri todos eles no começo. Qual é o problema aqui? Eu posso verificar se as gravações foram bem sucedidas.

edit: Eu não estava usando um bloco try-catch-finally. Eu tive as funções lançar uma exceção em seu lugar. Agora que eu coloquei o try-catch-finalmente em torno deles eles parecem estar fazendo melhor.

A maioria de vocês estava certa em abrir mais arquivos do que eu pensava. Ainda rastreando as coisas. Vou postar uma atualização depois de um tempo.

reedição: garantindo que todos os accesss a arquivos fossem agrupados com try-catch-finally corrigindo o problema. obrigado

No Linux e em outras plataformas semelhantes ao UNIX / UNIX, o SO coloca um limite no número de descritores de arquivos abertos que um processo pode ter a qualquer momento. Antigamente, esse limite costumava ser hardwired 1 e relativamente pequeno. Atualmente, ele é muito maior (centenas / milhares) e está sujeito a um limite de resources configurável por processo “suave”. (Olhe para cima a shell ulimit builtin …)

Seu aplicativo Java deve exceder o limite do descritor de arquivo por processo.

Você diz que tem 19 arquivos abertos, e que depois de algumas centenas de vezes você recebe uma IOException dizendo “muitos arquivos abertos”. Agora essa exceção específica só pode acontecer quando um novo descritor de arquivo é solicitado; isto é, quando você está abrindo um arquivo (ou um pipe ou um socket). Você pode verificar isso imprimindo o stacktrace para o IOException.

A menos que seu aplicativo esteja sendo executado com um pequeno limite de resources (o que parece improvável), segue-se que ele deve estar abrindo repetidamente arquivos / sockets / pipes e não fechando-os. Descubra por que isso está acontecendo e você deve ser capaz de descobrir o que fazer sobre isso.

FYI, o seguinte padrão é uma maneira segura de gravar em arquivos que garantem não vazar descritores de arquivos.

 Writer w = new FileWriter(...); try { // write stuff to the file } finally { try { w.close(); } catch (IOException ex) { // Log error writing file and bail out. } } 

1 – Hardwired, como compilado no kernel. Alterar o número de slots fd disponíveis exigia uma recompilation … e poderia resultar em menos memory disponível para outras coisas. Nos dias em que o Unix comumente rodava em máquinas de 16 bits, essas coisas realmente importavam.

ATUALIZAR

O Java 7 é mais conciso:

 try (Writer w = new FileWriter(...)) { // write stuff to the file } // the `w` resource is automatically closed 

ATUALIZAÇÃO 2

Aparentemente, você também pode encontrar um “muitos arquivos abertos” ao tentar executar um programa externo. A causa básica é como descrito acima. No entanto, o motivo pelo qual você encontra isso em exec(...) é que a JVM está tentando criar descritores de arquivo “pipe” que serão conectados à input / saída / erro padrão do aplicativo externo.

Você obviamente não está fechando seus descritores de arquivo antes de abrir novos. Você está no Windows ou no Linux?

Para UNIX:

Como Stephen C sugeriu, alterar o valor máximo do descritor de arquivo para um valor mais alto evita esse problema.

Tente observar sua capacidade atual de descritor de arquivo:

  $ ulimit -n 

Em seguida, altere o limite de acordo com suas necessidades.

  $ ulimit -n  

Observe que isso apenas altera os limites no shell atual e em qualquer processo filho / descendente. Para fazer a mudança “stick” você precisa colocá-lo no script de shell ou arquivo de boot relevante.

Embora na maioria dos casos gerais o erro seja claramente que as alças de arquivos não foram fechadas, acabei de encontrar uma instância com o JDK7 no Linux que bem … está suficientemente desenvolvida para explicar aqui.

O programa abriu um FileOutputStream (fos), um BufferedOutputStream (bos) e um DataOutputStream (dos). Depois de escrever para o dataoutputstream, o dos foi fechado e achei que tudo correu bem.

Internamente, no entanto, o dos, tentou liberar o bos, que retornou um erro Disk Full. Essa exceção foi comida pelo DataOutputStream e, como conseqüência, o banco subjacente não foi fechado, portanto, o fos ainda estava aberto.

Em um estágio posterior, esse arquivo foi renomeado de (algo com um .tmp) para seu nome real. Assim, os rastreadores de descritor de arquivo java perderam a trilha do arquivo .tmp original, mas ele ainda estava aberto!

Para resolver isso, tive que primeiro descarregar o DataOutputStream, recuperar a IOException e fechar o FileOutputStream eu mesmo.

Espero que isso ajude alguém.

Recentemente, eu tinha um arquivo de processamento de lote de programa, eu certamente fechei cada arquivo no loop, mas o erro ainda está lá.

E mais tarde, resolvi esse problema com garbage collection ansiosamente a cada centenas de arquivos:

 int index; while () { try { // do with outputStream... } finally { out.close(); } if (index++ % 100 = 0) System.gc(); }