Pergunta

Estou tentando inserir cerca de 50.000 objetos (e, portanto, 50.000 chaves) em um java.util.HashMap<java.awt.Point, Segment>. No entanto, continuo recebendo uma exceção de memória. (Segment é minha própria aula - muito leve - um String campo e 3 int Campos).

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:508)
    at java.util.HashMap.addEntry(HashMap.java:799)
    at java.util.HashMap.put(HashMap.java:431)
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

Isso parece bastante ridículo, pois vejo que há muita memória disponível na máquina - tanto no espaço de RAM e HD gratuito para memória virtual.

É possível que o Java esteja executando com alguns requisitos rigorosos de memória? Posso aumentar isso?

Existe alguma limitação estranha com HashMap? Vou ter que implementar o meu? Existem outras classes que valem a pena ver?

(Estou executando o Java 5 sob o OS X 10.5 em uma máquina Intel com 2 GB de RAM.)

Foi útil?

Solução

Você pode aumentar o tamanho máximo da pilha passando -xmx128m (onde 128 é o número de megabytes) para Java. Não me lembro do tamanho padrão, mas me impressiona que era algo bastante pequeno.

Você pode verificar programaticamente quanta memória está disponível usando o Tempo de execução classe.

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

(Exemplo de Java Developers Almanac)

Isso também é parcialmente abordado em Perguntas frequentes sobre a vm Java Hotspot, E no Página de ajuste Java 6 GC.

Outras dicas

Algumas pessoas estão sugerindo alterar os parâmetros do hashmap para reforçar os requisitos de memória. Eu sugeriria medir em vez de adivinhar; Pode ser algo mais causando o Oome. Em particular, eu sugiro usar qualquer NetBeans Profiler ou Visualvm (que vem com o Java 6, mas vejo que você está preso ao Java 5).

Outra coisa a tentar se você souber o número de objetos com antecedência é usar o construtor HashMap (int Capacidade, Double LoadFactor), em vez do não-Arg padrão que usa padrões de (16,0,75). Se o número de elementos no seu hashmap exceder (Capacidade * LoadFactor), a matriz subjacente no hashmap será redimensionada para a próxima potência de 2 e a tabela será reformulada. Essa matriz também requer uma área contígua de memória, por exemplo, se você estiver dobrando de um 32768 para uma matriz de tamanho 65536, precisará de um pedaço de 256kb de memória livre. Para evitar a alocação extra e as penalidades, basta usar uma tabela de hash maior desde o início. Também diminuirá a chance de você não ter uma área contígua de memória grande o suficiente para caber no mapa.

As implementações são apoiadas por matrizes normalmente. Matrizes são blocos de tamanho fixo de memória. A implementação do HashMap começa armazenando dados em uma dessas matrizes em uma determinada capacidade, digamos 100 objetos.

Se preencher a matriz e você continuar adicionando objetos, o mapa precisará aumentar secretamente o tamanho da matriz. Como as matrizes são corrigidas, faz isso criando uma matriz totalmente nova, na memória, juntamente com a matriz atual, que é um pouco maior. Isso é chamado de crescimento da matriz. Em seguida, todos os itens da matriz antiga são copiados para a nova matriz e a matriz antiga é desreferenciada com a esperança de que seja coletada de lixo e a memória liberada em algum momento.

Geralmente, o código que aumenta a capacidade do mapa copiando itens em uma matriz maior é a causa de tal problema. Existem implementações "idiotas" e as inteligentes que usam um fator de crescimento ou carga que determina o tamanho da nova matriz com base no tamanho da matriz antiga. Algumas implementações ocultam esses parâmetros e outros não, para que você nem sempre as configure. O problema é que, quando você não pode defini -lo, ele escolhe algum fator de carga padrão, como 2. Portanto, a nova matriz é o dobro do tamanho do antigo. Agora, seu mapa supostamente 50k tem uma matriz de apoio de 100k.

Olhe para ver se você pode reduzir o fator de carga para 0,25 ou algo assim. Isso causa mais colisões de mapas de hash que prejudica o desempenho, mas você está atingindo um gargalo de memória e precisa fazê -lo.

Use este construtor:

(http://java.sun.com/javase/6/docs/api/java/util/hashmap.html#hashmap(int, flutuar)))

Você provavelmente precisa definir o sinalizador -xmx512m ou algum número maior ao iniciar o Java. Eu acho que 64MB é o padrão.

Editado para acrescentar: Depois de descobrir quanta memória seus objetos estão realmente usando com um perfil, convém analisar referências fracas ou referências suaves para garantir que você não esteja acidentalmente mantendo parte de sua memória refém do coletor de lixo quando Você não está mais usando eles.

Também pode querer dar uma olhada nisso:

http://java.sun.com/docs/hotspot/gc/

Implícito nessas respostas, o Java tem um tamanho fixo para a memória e não cresce além do tamanho máximo de pilha configurado. Isso é diferente, digamos, C, onde é limitado apenas pela máquina em que está sendo executada.

Por padrão, a JVM usa um espaço de heap limitado. O limite é dependente da implementação da JVM e não está claro qual JVM você está usando. No OS, além do Windows, uma JVM Sun de 32 bits em uma máquina com 2 GB ou mais usará um tamanho máximo padrão de 1/4 da memória física, ou 512 Mb no seu caso. No entanto, o padrão para uma JVM de modo "cliente" é de apenas 64 MB de tamanho de pilha, o que pode ser o que você encontrou. Os JVMs de outros fornecedores podem selecionar padrões diferentes.

Obviamente, você pode especificar o limite de heap explicitamente com o -Xmx<NN>m opção para java, Onde <NN> é o número de megabytes para a pilha.

Como um palpite, sua tabela de hash deve estar usando apenas 16 MB; portanto, deve haver outros objetos grandes na pilha. Se você pudesse usar um Comparable chave em a TreeMap, isso economizaria alguma memória.

Ver "Ergonomia na JVM 5.0" para mais detalhes.

O espaço da pilha Java é limitado por padrão, mas isso ainda parece extremo (embora quão grandes sejam seus 50000 segmentos?)

Suspeito que você tenha algum outro problema, como as matrizes no conjunto crescendo muito, porque tudo é atribuído ao mesmo "slot" (também afeta o desempenho, é claro). No entanto, isso parece improvável se seus pontos forem distribuídos uniformemente.

Eu estou me perguntando por que você está usando um hashmap em vez de um treemap? Embora os pontos sejam bidimensionais, você pode subclasse -os com uma função de comparação e depois fazer pesquisas de log (n).

Pensamento aleatório: os baldes de hash associados ao hashmap não são particularmente eficientes em memória. Você pode experimentar o TREEMAP como alternativa e ver se ele ainda fornece desempenho suficiente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top