Olá,
Estou com uma dúvida sobre um determinado processo. Eu tenho uma rotina que onde eu preciso obter todo o conteúdo armazenado num resultset e passar para um objeto que vai fazer um determinado tratamento. Para evitar a carga do select toda de uma vez esse resultset foi sendo gerado através de um cursor de forma que passou a carregar todos os registros de 10 em 10; até ai tudo bem. Meu problema começou ao descobrir que esta rotina ainda seria capaz de lançar um OutOfMemoryError.
Fui dar uma olhada na rotina e notei que, aparentemente, o problema não deveria ocorrer (ao menos não pude encontrar nada que me sugerisse isso). Segue um algoritmo que demonstra o que a rotina realiza, basicamente, (não posso colocar o próprio código por causa de um contrato assinado com a empresa):
String[] colunas = colunas utilizadas no cursor
StringBuffer linha;
ResultSet rs;
BufferedWriter bf;
while ( ( rs = fetch 10 in cursor ).next() ){
StringBuffer linha = new StrinBuffer();
for( int i = 0; i < colunas.length; i++ ){
linha.append( getConteudoProcessado( rs.getString( colunas[ i ] ) ) );
}
bf.write( linha.toString() );
rs.close(); //coloquei isso pq achei que o ResultSet estava deixando recursos alocados
}
Esse StringBuffer recebe o append várias vezes ao invés de apenas 1... mas o seu conteúdo não chega a um valor significativo. O método getConteudoProcessado ilustra a situação onde o valor obtido do resultset é processado por vários objetos (atributos de instância) cuja finalidade é somente processar e retornar uma String.
Utilizei o seguinte comando: -XX:+HeapDumpOnOutOfMemoryError para tirar um "retrato" da memória no momento do OutOfMemory.
As minhas dúvidas são:
1 - O fato de sempre instanciar um StringBuffer novamente é melhor ou limpá-lo deveria ser a melhor estratégia? Pq?
2 - O BufferedWriter consegue descarregar o conteúdo em disco conforme seu buffer enche? Se sim, é necessário configurar um tamanho desse buffer ou o tamanho padrão deve servir?
3 - A chamda rs.close() é, realmente, necessária nesta situação?
4 - Se a VM sentir necessidade de alocar mais espaço, ela deve rodar o GC para tentar liberar algum espaço, correto? É possível que algum desses objetos não seja coletado?
5 - O Arquivo gerado pelo comando -XX:+HeapDumpOnOutOfMemoryError apresenta o estado da memória heap já com a execução do GC? Li em algum lugar que antes de lançar OutOfMemoryError a VM ainda executa o GC, mas o resultado do dump apresenta a memória após a sua execução ou pode ser que o GC ainda não tenha sido executado?
6 - Mesmo tendo apresentado esse trecho que representa o processo bem superficialmente, é possível dizer que um cursor cujo select tenha 4000 registros e 4 vezes mais rápido que um que possua 16000 registros?
Espero ter sido claro o suficiente,
Grato pela atenção,
Éberson