Pergunta

Em meu lugar onde eu trabalho, costumava ter arquivos com mais de milhão de linhas por arquivo. Mesmo que a memória do servidor são mais do que 10 GB com 8 GB por JVM, por vezes, o servidor se enforcado por alguns momentos e sufoca as outras tarefas.

Eu perfilado o código e descobriu que ao ler o arquivo uso de memória aumenta em Giga Bytes de frequência (1 GB para 3 GB) e, de repente volta ao normal. Parece que esta alta frequente e usos de pouca memória trava meus servidores. Claro que isso foi devido a coleta de lixo.

Qual API devo usar para ler os arquivos para um melhor desempenho?

Righ agora eu estou usando BufferedReader(new FileReader(...)) para ler esses arquivos CSV.

Processo:? Como eu estou lendo o arquivo

  1. I ler arquivos linha por linha.
  2. Cada linha tem poucas colunas. com base nos tipos I analisá-los correspondentemente (coluna custo em dobro, coluna visita em int, coluna de palavra-chave em String, etc ..).
  3. Eu empurro o conteúdo elegíveis (visita> 0) em um HashMap e, finalmente, limpa que Map no final da tarefa

Atualizar

Eu faço essa leitura de 30 ou 31 arquivos (dados de um mês) e armazenar o elegível num mapa. Mais tarde, este mapa é usado para obter alguns culpados em tabelas diferentes. Portanto leitura é obrigação e armazenar esses dados é também obrigação. Apesar de eu ter mudado a parte HashMap para BerkeleyDB agora, mas a questão no momento da leitura do arquivo é o mesmo ou ainda pior.

Foi útil?

Solução

BufferedReader é um dos dois melhores APIs para usar para isso. Se você realmente teve problemas com a leitura de arquivos, uma alternativa poderia ser a de usar o material em NIO a memória de mapear seus arquivos e, em seguida, ler o conteúdo diretamente de memória.

Mas o problema não é com o leitor. Seu problema é que cada operação de leitura cria um monte de novos objetos, provavelmente em material que você faz apenas após a leitura.

Você deve considerar a limpeza de seu processamento de entrada com um olho na redução do número e / ou tamanho dos objetos que você criar, ou simplesmente se livrar de objetos com mais rapidez quando já não são necessários. Seria possível para efectuar a sua uma linha arquivo ou pedaço de cada vez ao invés de inalar a coisa toda na memória para processamento?

Outra possibilidade seria a de mexer com coleta de lixo. Você tem dois mecanismos:

  • Explicitamente chamar o coletor de lixo de vez em quando, digamos, a cada 10 segundos ou a cada 1000 linhas de entrada ou algo assim. Isto irá aumentar a quantidade de trabalho realizado pela GC, mas vai demorar menos tempo para cada GC, sua memória não vai inchar tanto e por isso espero que haverá menos impacto sobre o resto do servidor.

  • Fiddle com opções de coletor de lixo da JVM. Estes diferem entre JVMs, mas java -X deve dar-lhe algumas dicas.

Update: A maioria abordagem promissora:

Você realmente precisa de todo o conjunto de dados na memória de uma só vez para o processamento?

Outras dicas

Eu perfilado o código e descobriu que enquanto arquivo de leitura uso de memória aumenta em Giga Bytes frequentemente (1GB a 3 GB) e então de repente volta ao normal. isto parece que esta alta frequência e baixa usa memória trava meus servidores. Do Claro que isso foi devido a lixo coleção.

Usando BufferedReader(new FileReader(...)) não irá causar isso.

Eu suspeito que o problema é que você está lendo as linhas / linhas em uma matriz ou lista, processá-los e, em seguida, descartar a matriz / lista. Isso fará com que o uso de memória para aumentar e depois diminuir novamente. Se este for o caso, você pode reduzir o uso de memória, processamento de cada linha / linha como você lê-lo.

Editar : Estamos de acordo que o problema é sobre o espaço usado para representar o conteúdo do arquivo na memória. Uma alternativa para uma enorme hashtable em memória é voltar para o velho "merge sort" se aproximar que usamos quando a memória do computador foi medida em Kbytes. (Estou assumindo que o processamento é dominado por uma etapa em que você está fazendo uma pesquisa com as chaves K para obter o associado linha R).

  1. Se necessário, pré-processar cada um dos arquivos de entrada, para que possam ser classificadas na chave K.

  2. Use um utilitário de arquivo tipo eficiente para classificar todos os arquivos de entrada em ordem sobre a K. Você quer usar um utilitário que irá utilizar um algoritmo merge sort clássica. Isso vai dividir cada arquivo em pedaços menores que podem ser classificados na memória, classificar os pedaços, gravá-los em arquivos temporários, em seguida, mesclar os arquivos temporários ordenados. O utilitário sort UNIX / Linux é uma boa opção.

  3. Leia os arquivos ordenados em paralelo, leitura todas as linhas que se relacionam com cada valor de chave de todos os arquivos, processá-los e, em seguida, pisar para o valor da chave seguinte.

Na verdade, estou um pouco surpreso que o uso de BerkeleyDB não ajudou. No entanto, se profiling diz-lhe que a maior parte do tempo estava indo na construção da DB, você pode ser capaz de acelerá-lo, classificando o arquivo de entrada (como acima!) Em ordem crescente de chave antes de construir o DB. (Ao criar um grande índice baseado em arquivo, você obter um melhor desempenho se as entradas são adicionadas na ordem da chave.)

Tente usar as seguintes opções vm, a fim de ajustar o gc (e fazer alguma impressão gc):

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top