Java.io.IOException: Too many open files - Como fazer "close()"?

Estou executando a script abaixo para atualização em vários diretórios de uma máquina Sun.
Porém, se o número de diretórios for grande (+/- 1000), ocorre o erro após algumas execuções do comando:
java.io.IOException: Too many open files
Como fazer para ir “fechando” os processos e evitar o erro?

O que encontrei no Google foi:
“I suspect the problem is that the call to Runtime.exec() creates stream
handles for the standard input, output and error streams. You should
make sure to close these streams in your code. (Capture the Process
object returned from Runtime.exec() to get access to these streams.)”

Como faço isso?

Segue o código:

boolean recursive = true;
File root = new File ("C:/");
File pastaInicial = new File ("/inicial");
String cmd = "";
try {
	if (root.exists()) {
		File[] uf = root.listFiles();
		for(int u = 0; u < uf.length; u++) {
			if(recursive && uf[u].isDirectory()) {
				File[] gestor = uf[u].listFiles();
				for(int g = 0; g < gestor.length; g++) {
					String nomeGestor = gestor[g].getName();
					if (recursive && gestor[g].isDirectory()) {
						File[] id = gestor[g].listFiles();
						for (int i = 0; i < id.length; i++) {
							if(recursive && id[i].isDirectory()) {
								File pastaNova = new File (id[i].getPath() + "/nova");
									cmd = "ln -s " + pastaInicial + " " + pastaNova;
									try {
			             							Runtime rt = Runtime.getRuntime();
										Process proc = rt.exec(cmd);
       										}
									catch (Exception e) {
										out.println("Ocorreu erro: " + e.toString() + "<br>");
									}
							}
						}
					}
				}
			}
		}
	} else {
		out.println("Pasta " + root +" não encontrada.<br>");
	}
}
catch(Exception e) {
	out.println("Ocorreu erro: " + e.toString());
}

Grato.

Eu recomendaria escrever um programa que fizesse três coisas:

  • Escrevesse um script contendo a seqüência de suas ações (que são basicamente ln -s <arg1> <arg2> )
  • Alterasse a permissão de uso do script, ou então usasse um outro script que o interpretasse (com o comando “.” talvez), se você não puder alterar a permissão de uso;
  • Executasse o script gerado.
  • E talvez deletasse o script gerado.

É mais rápido e você tem um subproduto que é o script gerado, que você pode usar para conferir o que foi feito.

Aliás, que script esquisito é esse que usa um arquivo “C:/” ? Sempre achei que os diretórios em Unix começavam com “/” ou então com “//” se for usado NFS e estiver em uma máquina externa (mas é a sintaxe do rcp).
No caso do Solaris, então, normalmente o acesso a arquivos externos é algo como “/net/” alguma coisa.

EDIT - Não me expliquei direito. Você chama N vezes o Runtime.exec. Em vez disso, crie um script gigantesco que contém todos os comandos ln -s que você precisa dar. Então você vai ter de chamar Runtime.exec apenas 2 vezes: a primeira para alterar a permissão do script (“chmod +x …”), e a segunda para chamar o seu script ("/bin/ksh -c …"). Isso vai fazer com que os seus problemas sejam minimizados.

Foi mal o C:/ - Esqueci de editar… :roll:
Mas mesmo executando um script externo, estaria chamando um runtime. Como poderia fechá-lo a cada pasta que executasse o comando?

Além da sugestão do thingol ser boa, o que vejo é que você não está diretamente pegando um Input/Output Stream do console, então não tem porque fechá-lo.

O que está causando isso não é o stream, mas sim os trocentos File. Você tem é que fechar o File quando não rpecisar mais dele. Todo processo (i.e. JVM) tem um limtie de arquivos que pdoe abrir ao mesmo tempo.

Cara, pq vc ta fazendo isso em Java!? Isso eh UMA linha de shell script :mrgreen:

for i in `find .`; do faca_algo_com "$i" ; done

Nem tinha pensado isso (find . -name -print -exec ) …
É que normalmente usar find. -name -print -exec requer um pouco de prática - sabe como é que é, não é todo mundo que entende o seguinte comando:

grep -i main `find . -name \*.java -print`

ou (este a seguir não imprime os nomes dos arquivos, mas é só ver a opção correta a passar para grep):

find . -name \*.java -exec grep main {} \;

que equivale ao comando em Windows:

findstr /s /c:main *.java

ou seja, efetua um “grep” recursivo.

E, se vc quer um grep recursivo, pq nao o bom e velho grep -R? :mrgreen:

http://www.codecomments.com/archive257-2005-4-226267.html

Grep recursivo (-R) - hum, eu sou velho mesmo, não tinha visto que isso existe no Linux (e de modo geral, em todas as versões do grep que são descendentes do código GNU). Se bobear até o grep do Solaris 10 já tem isso também.
Nada que um “man grep” não resolva.
Mas é só para dar a idéia.

NAs versões tradicionais de UNIX: egrep :wink:

http://forum.java.sun.com/thread.jspa?threadID=533029&messageID=2572018

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692