Problemas com muitos objetos em Listas (Java Heap Space)

Olá pessoal, vou descrever o cenário para que vocês possam quem sabe me dar um auxilio:

Problema 1.

Tenho um sistema que faz a leitura de arquivos no formato csv ou txt respectivamente separados por [i]ponto e virgula[/i], estes arquivos possuem tamanhos variáveis que vão de 200.000 a 1.000.000 de linhas de dados, o que ocorre é que eu estou utilizando para separar estes dados, o comando split(";"), porem quando o arquivo é muito grande, ele começa a dar o famoso estouro de heap e ai tenho que aumentar a memória da JVM. só que além do split ainda preciso remover alguns caracteres e fazer outras conversões de tipos na leitura depois do split e então esta havendo um consumo muito grande de memória no sistema.

Problema 2
Outro problema que estou tendo é que depois da leitura dos arquivos citados acima, eu os armazeno em listas List e também, não somente neste procedimento mas sim em toda a aplicação faço o uso de listas de objetos para passagem de parâmetros entre métodos, até que estes dados cheguem ou retornem do banco de dados, porem estou com duvidas se este procedimento é o mais indicado para tal. Pois devido ao grande volume de dados estou tendo problemas de Estouro de Heap.

Para contornar o problema 1 e 2 utilizei os parametros da JVM: -Xmx e -Xms para aumentar a memória da mesma, porem gostaria de saber se existe alguma outra forma de resolver isso? atualmente o sistema é utilizado no modelo cliente/servidor, através do protocolo jnlp o pessoal faz o download das atualizações para a maquina local, ai fica complicado configurar isso sempre que alguem faz um novo download do aplicativo. Outro detalhe é que estou querendo migrar o mesmo para uma aplicação com front-end para web para que os processos sejam feitos todos no servidor, e ai estou com receio de ficar mais complicado ainda devido ao grande numero de dados como citado acima.

Duvida 3.
O Uso excessivo de casting como nos exemplos abaixo consomem mais memória? são praticas aplicáveis ou recomendáveis?:

public Object getProduto(int idEmpresa) {
    // procedimentos para buscar no banco
    return new Produto(rs.getInt("id_produto"), rs.getString("descricao"));
}

// em algum lugar da aplicação

Produto meuProduto = (Produto) getProduto(10);

ou deste tipo:

public void inserir(Object obj) {
  Connection con;
  PreparedStatement pst = null;
  
  con = Conexao.getConnection();
  pst = con.prepareStatement("insert into produtos (id_produto, descricao) values (?,?)");
  pst.setInt(1, ((Produto) obj).getIdProduto());
  pst.setString(2, ((Produto) obj).getDescricao());
  pst.executeUpdate();
}

ou este tipo de utilização em excesso no sistema pode dar problemas, qual a opinião de vocês quanto a isto, existe uma solução melhor para isso, para acesso a dados estou utilizando jdbc.

Att,
André Dalcin

Você está armazenando em memória o conteúdo completo dos arquivos CSV / TXT, para processar depois os dados acumulados? Não acumule os dados, capture um registro do arquivo CSV / TXT de cada vez e processe a informação imediatamente, dessa maneira você não precisa guardar o arquivo completo em memória.
http://www.guj.com.br/java/250304-erro-ao-ler-um-arquivo-txt–exception-in-thread-main-javalangoutofmemoryerror-java-heap-

Amigo…

[quote]O que vai acontecer quando vc colocar 1 litro de coca num copo de 300 ML?[/quote] kkkkkkkkkkkkk
É claro q não tem !!! Se seu processo ta gastando mais memoria do que a alocada para JVM…como fica?
Unica saida é vc tentar economizar memoria usando práticas de otimização…mas mesmo assim no fim, mesmo otimizado pode ainda gastar mais do que vc tem…nesse caso tem alocar…

Problema 1.
Vc ta usando StringBuffer?
Vc precisa realmente manter todo o conteúdo na memoria?
Não da para fazer paginação?
Vc ta liberando os ponteiros logo apos verificar q não tem mais uso?

Duvida 3.
Eu não tenho informações concretas e nem links sobre isso, na verdade nunca vi…mas cast não gasta memoria! Simplesmente vc reaponta ponteiros ja alocados…

[quote=roger_rf]Você está armazenando em memória o conteúdo completo dos arquivos CSV / TXT, para processar depois os dados acumulados? Não acumule os dados, capture um registro do arquivo CSV / TXT de cada vez e processe a informação imediatamente, dessa maneira você não precisa guardar o arquivo completo em memória.
http://www.guj.com.br/java/250304-erro-ao-ler-um-arquivo-txt–exception-in-thread-main-javalangoutofmemoryerror-java-heap-[/quote]

Olá, obrigado pela resposta, estou fazendo da seguinte forma.

  1. Lendo Linha a Linha o arquivo csv ou txt
  2. Cada linha lida recebe um split(";"); que é armazenado em um vetor
  3. Instancio um Objeto da classe Produto.
  4. Passo todos os dados que preciso do Array gerado pelo split() para o produto
  5. Armazeno este Objeto produto em uma lista de Objetos
  6. O Procedimento se repete para todas as linhas do arquivo

Após este procedimento esta lista passa para outro método em outra classe que ira receber um tratamento especifico, e só depois vai para o DAO que insere estes dados no DB.

O detalhe é que esta lista fica muito grande na memória devido ao grande numero de linhas do arquivo. Inclusive eu faço troca de dados de colunas de acordo com alguns parâmetros que são advindos do banco de dados.

Att,
André Dalcin

Então Andre
Vc tem 2 opções…

1) Aumentar a memoria
Complicado aqui…uma vez que quanto maior o arquivo, mais RAM na JVM vc vai precisar…então na verdade não resolve…apenas remedia temporariamente.

2) Reduzir os gastos
Aqui ja podemos caminhar melhor…
Por exemplo…pq vc não pagina o arquivo?
Tipo…vc pode delimitar 800 linhas por vez… e salva no banco.
Volta e refaz as próximas 800…
Vai demorar mais, mas vc garante robustez e estabilidade.

3) DAO
Vc ta otimizando JDBC?
Reusando objetos?
Como vc ta fazendo esse dao? ta usando insert unitários dentro de um loop? espero q não.
Ta usando comando em BATCH? Deveria…
http://www.roseindia.net/jdbc/Jdbc-batch-insert.shtml

[quote=FernandoFranzini]Amigo…

[quote]O que vai acontecer quando vc colocar 1 litro de coca num copo de 300 ML?[/quote] kkkkkkkkkkkkk
É claro q não tem !!! Se seu processo ta gastando mais memoria do que a alocada para JVM…como fica?
Unica saida é vc tentar economizar memoria usando práticas de otimização…mas mesmo assim no fim, mesmo otimizado pode ainda gastar mais do que vc tem…nesse caso tem alocar…

Problema 1.
Vc ta usando StringBuffer?
Vc precisa realmente manter todo o conteúdo na memoria?
Não da para fazer paginação?
Vc ta liberando os ponteiros logo apos verificar q não tem mais uso?

Duvida 3.
Eu não tenho informações concretas e nem links sobre isso, na verdade nunca vi…mas cast não gasta memoria! Simplesmente vc reaponta ponteiros ja alocados…
[/quote]

Olá, muito obrigado pela resposta:

Não estou utilizando StringBuffer, estou armazenando os dados lidos da linha pelo split() em Objetos do tipo Produto, e estes são armazenados em uma lista.

Bom como comentei acima, preciso tratar algumas situações como troca de valores de cada um destes itens de uma posição para outra.

Em que consiste a paginação?

Sim e Não, dentro do corpo dos métodos eu estou passando null para os objetos variáveis que não estou mais utilizando, existe outra forma de liberar os ponteiros destes objetos??

Att,
André Dalcin

a dica de paginação esta no meu post acima.

Outra sugestão: em vez de guardar os objetos intermediários numa lista, grave-os numa tabela temporária de BD. Depois você pode tratar o conteúdo desta tabela temporária antes de descarregá-la na tabela final.

[quote=FernandoFranzini]Então Andre
Vc tem 2 opções…

1) Aumentar a memoria
Complicado aqui…uma vez que quanto maior o arquivo, mais RAM na JVM vc vai precisar…então na verdade não resolve…apenas remedia temporariamente.

2) Reduzir os gastos
Aqui ja podemos caminhar melhor…
Por exemplo…pq vc não pagina o arquivo?
Tipo…vc pode delimitar 800 linhas por vez… e salva no banco.
Volta e refaz as próximas 800…
Vai demorar mais, mas vc garante robustez e estabilidade.

3) DAO
Vc ta otimizando JDBC?
Reusando objetos?
Como vc ta fazendo esse dao? ta usando insert unitários dentro de um loop? espero q não.
Ta comando em comando em BATCH?
http://www.roseindia.net/jdbc/Jdbc-batch-insert.shtml[/quote]

Novamente obrigado pela pronta resposta;

Vou verificar se consigo fazer isso devido aos metodos que tenho a frente desta leitura de arquivo.

Sobre a otimização do JDBC devo admitir que não estava usando o batch, desconhecia o mesmo, vou iniciar a implementação do mesmo agora.
Quanto ao reuso de Objetos vc poderia me dar uma ideia?? sobre???

Att,
André Dalcin

Olá, obrigado pela dica, estou pensando em fazer isso mesmo. Vlw

Att,
André Dalcin

Olá, estou com um problema parecido ao do GodZilla_XF, estou lendo um arquivo txt que possui como separador um pipe (|), estou usando string.split("\|"), porém essa abordagem tem um problema, alguns registros podem vir vazios por exemplo: |XXX|0001|abc||||||||. Ele deveria reconhecer os registros vazios mas não reconhece, então eu fiz uma gambi e coloquei um “;” no final da string pra ele poder reconhecer os registros vazios. Mas enfim não é esse o problema… Estou fazendo o seguinte:

  1. Leio um arquivo txt e gero um StringBuffer;
  2. uso o stringbuffer para ler uma linha (readline()) e coloco em uma string.
  3. coloco o “;” no final da linha (string += “;”)
  4. uso o string.split("\|") (uso o mesmo vetor a cada linha)
  5. como cada atributo da linha é uma informação eu adiciono todos a uma listaDeAtributos (List) (utilizo o mesmo objeto list , só uso o .clear() antes)
  6. crio um objeto, colocando os atributos da linha nos atributos do objeto.
  7. coloco o objeto numa listaDeObjetos (List)
  8. no final mando salvar todos os objetos da listaDeObjetos através de um DAO, utilizando hibernate.

O problema é que o arquivo txt é muito grande, tem + de 2.000.000 de linhas, o meu programa não consegue nem chegar no método de salvar, e ocorre Exception in thread “main” java.lang.OutOfMemoryError: Java heap space.

Já tentei aumentar a memória da JVM usando os parâmetros -Xms e -Xmx, mas não adiantou. Alguma sugestão ?

Perdão no meu post acima não é StringBuffer na verdade é BufferedReader. Sorry.

[quote=rod_ufpa]Olá, estou com um problema parecido ao do GodZilla_XF, estou lendo um arquivo txt que possui como separador um pipe (|), estou usando string.split("\|"), porém essa abordagem tem um problema, alguns registros podem vir vazios por exemplo: |XXX|0001|abc||||||||. Ele deveria reconhecer os registros vazios mas não reconhece, então eu fiz uma gambi e coloquei um “;” no final da string pra ele poder reconhecer os registros vazios. Mas enfim não é esse o problema… Estou fazendo o seguinte:

  1. Leio um arquivo txt e gero um StringBuffer;
  2. uso o stringbuffer para ler uma linha (readline()) e coloco em uma string.
  3. coloco o “;” no final da linha (string += “;”)
  4. uso o string.split("\|") (uso o mesmo vetor a cada linha)
  5. como cada atributo da linha é uma informação eu adiciono todos a uma listaDeAtributos (List) (utilizo o mesmo objeto list , só uso o .clear() antes)
  6. crio um objeto, colocando os atributos da linha nos atributos do objeto.
  7. coloco o objeto numa listaDeObjetos (List)
  8. no final mando salvar todos os objetos da listaDeObjetos através de um DAO, utilizando hibernate.

O problema é que o arquivo txt é muito grande, tem + de 2.000.000 de linhas, o meu programa não consegue nem chegar no método de salvar, e ocorre Exception in thread “main” java.lang.OutOfMemoryError: Java heap space.

Já tentei aumentar a memória da JVM usando os parâmetros -Xms e -Xmx, mas não adiantou. Alguma sugestão ?[/quote]

Você precisa fazer uma inserção de 2 milhões de registros em um banco? É necessário fazer algum tratameno nesses dados antes de salvá-los, ou é suficiente simplemente normalizar alguns campos
Em vez de você usar Hibernate, faça o seguinte: transforme seu arquivo de entrada em um outro que seja aceito por alguma ferramenta do seu banco de dados que faça importação de dados.
Aí você precisa consultar a documentação do banco que você vai usar.
Normalmente bancos de dados aceitam o formato CSV, que é muito simples de gerar.
A importação será muito mais rápida; você pode até, para automatizar o processo, chamar a ferramenta do banco através de seu programa. Garanto que isso será muito mais rápido e eficiente, tanto em termos de banco quanto em termos de seu programa.

Desculpe o atraso para responder, eu consegui resolver o meu problema fazendo algumas alterações nos parâmetros da jvm. Em termos de eficiência ele demora um pouco (entre 30s a 1 minuto) mas consegue fazer a leitura, ainda não testei a persistência. Em relação a transformação do arquivo para um formato como CSV, é uma boa idéia, porém o programa deve fazer algumas verificações durante a leitura, por isso preferimos gerar um programa para fazer isso. Até mesmo porque vai haver muita leitura e alteração dos dados e no final deve ser gerado outro txt. Acredito que realmente a abordagem pelo arquivo CSV fosse mais rápida. Obrigado pela dica.