Ler arquivo texto grande [RESOLVIDO]

Estou com um problema para ler um arquivo texto com cerca de 800MB.

Estou usando a seguinte rotina:

BufferedReader br = new BufferedReader(new FileReader(file));
while ((linha = br.readLine()) != null) {
// aqui faço umas verificações com a pedaços da linha
// faço alguns substrings e vejo se o valor está em determinado padrão
}

Não consigo rodar isso, antes de chegar na metade do arquivo dá um Java Heap Space.
Porém não posso simplesmente aumentar a memória das configurações do glassfish porque a aplicação vai precisar suportar qualquer tamanho de arquivo.

O BufferedReader vai lendo as linhas do arquivo texto e guarda na memória ?
Existe uma forma mais eficiente de fazer essa leitura ?

Obrigado

BufferedReader usa apenas 8 Kbytes, mais ou menos, de memória, a menos que você estabeleça um buffer maior. Deve ser alguma coisa que você fez.

Você está guardando a String linha em algum array ou um Collection? Você está usando recursividade? Se nenhuma das duas isso não deveria acontecer.

Como já foi dito não é o BufferedReader que está causando o problema…

Sim… utilize NIO http://java.sun.com/javase/6/docs/api/java/nio/package-summary.html para ler este arquivo grande. Você terá um ganho significativo de performance.

Vou fazer um pequeno parêntese, que não tem absolutamente nada a ver com o problema que você está tendo.

Digamos que você leia uma string de 1.000.000 caracteres e pegue dela apenas uma substring de 100 caracteres.

String s = ...;
String pequena = s.substring (100, 200);
s = null;

Quanto espaço, após fazer “s = null”, vai ser ocupado na memória?

a) 100 caracteres - quando você cria a nova string, copia os caracteres da string antiga e a referência à string grande se perde.
b) 1M caracteres - quando você cria a nova string, ela simplesmente é um ponteiro para o início e o fim dos caracteres da string antiga. Então, mesmo você removendo a referência à string antiga, o array de caracteres que é contido pela string antiga não se perde.

Por incrível que pareça, a resposta é “b)”.
É que normalmente isso não ocorre, mas é bom você ficar esperto.
Se tal caso se repetir muito, você até pode tentar algo como:

String pequena = new String (s.substring (100, 200).toCharArray());

Nojento, não?

Basicamente essas verificações que faço são do tipo:

    while ((linha = br.readLine()) != null) {

        valor = pegaPosicao(linha, 22, 141);
        if (valor.equals("")) {
            escreveCritica("Erro na linha " + iLinha + ". Nome do Beneficiário em Branco", true);
            setErros(getErros() + 1);
        }
        // cerca de 20 blocos como esse

    }

    public String pegaPosicao(String valor, Integer posInicial, Integer posFinal) {
        return valor.substring(posInicial - 1, posFinal).trim();
    }

    public void escreveCritica(String conteudo, Boolean adiciona) throws IOException {
        PrintWriter out = new PrintWriter(new FileWriter(getCritica(), adiciona));
        out.println(conteudo);
        out.close();
        out = null;
    }

Já testei se o problema era no método escreveCritica comentando tudo que estava lá dentro, mas o erro persistiu.

Então deveria fazer algo como:

valor = new String (pegaPosicao(linha, 22, 141).toCharArray());

Estou declarando essa String antes do while(), deveria declarar dentro ?

Obrigado

private static final String emptyString = "";
public String pegaPosicao(String valor, Integer posInicial, Integer posFinal) {
     String s = valor.substring (posInicial - 1, posFinal).trim();
     if (s.isEmpty()) return emptyString;
     else return new String (valor.toCharArray());
} 

Obrigado entanglement, coloquei pra rodar de novo e está em um ponto que está usando 300MB de memória, sendo que antes das alterações já estaria com mais de 600MB

Só corrigindo uma coisa no método:

    public String pegaPosicao(String valor, Integer posInicial, Integer posFinal) {
        String s = valor.substring (posInicial - 1, posFinal).trim();
        if (s.isEmpty()) return emptyString;
        else return new String (s.toCharArray());
    }

no que vc colocou estava retornando valor.toCharArray()

Obrigado e fica a dica pra quem precisar.