Por que o tamanho do heap é fixo nas JVMs?


20

Alguém pode me explicar por que as JVMs (eu não verifiquei muitas, mas nunca vi uma que não fizesse dessa maneira) precisam executar em um tamanho de heap fixo? Eu sei que é mais fácil implementar em um heap contíguo simples, mas a Sun JVM agora tem mais de uma década, então eu esperaria que eles tivessem tempo para melhorar isso.

A necessidade de definir o tamanho máximo de memória do seu programa no momento da inicialização parece algo da década de 1960, e existem as más interações com o gerenciamento de memória virtual do SO (GC recuperando dados trocados, incapacidade de determinar quanta memória o processo Java é realmente usando do lado do sistema operacional, enormes quantidades de espaço da VM desperdiçadas (eu sei, você não se importa com suas máquinas sofisticadas de 48 bits ...)). Também acho que as várias tristes tentativas de construir pequenos sistemas operacionais dentro da JVM (servidores de aplicativos EE, OSGi) são, pelo menos parcialmente, responsáveis ​​por essa circunstância, porque a execução de vários processos Java em um sistema invariavelmente leva a desperdícios de recursos, porque é necessário dê a cada um deles a memória que ele pode ter para usar no pico.

Surpreendentemente, o Google não produziu as tempestades de indignação com isso que eu esperaria, mas elas podem ter sido enterradas sob os milhões de pessoas que descobriram sobre o tamanho fixo da pilha e aceitaram isso por um fato.


O design dos servidores de aplicativos EE faria todo o sentido, mesmo sem essa "circunstância", pois a própria JVM precisa de algum espaço, e alternar entre threads é mais barato do que alternar entre processos - essa é uma das coisas que tornou o Java grande no final dos anos 90.
Michael Borgwardt

Isso é meio que divertido, e eu me pergunto quanto você pensou nisso. Por exemplo, como a interação GC / troca mudaria se você não tivesse um limite de heap? Como o espaço da VM é desperdiçado? Você obtém seu espaço de 2 / 3Gb, independentemente de usá-lo ou não, e se você está ampliando os limites desse espaço, não importa se você tem um heap fixo ou flutuante. Nesse caso, como várias JVMs desperdiçam algo que não seja swap (que deve ser configurado adequadamente para os fins da máquina)?
Kdgregory

2
É uma espécie de discurso retórico, mas é informado por anos de experiência diária na escrita e operação de uma plataforma baseada em Java. Se você não fizer a troca (porque ele deixará o sistema sem resposta por 20 minutos até que o processo descontrolado fique sem espaço para alocar) e a sobrechamada de memória seja desativada por motivos de estabilidade (o assassino do OOM não é muito bom em escolher vítimas) ), você se preocupa com o espaço da VM e, ao contrário do que as pessoas abaixo estão sugerindo, o lançamento de uma Java VM com -Xmx2048m alocará imediatamente 2 GB de memória virtual (pelo menos na minha Sun JVM no Linux) para um programa com uma variável.
precisa

Excelente pergunta. Estive pensando a mesma coisa. Mas qual dos "fatos" apresentados aqui em q e as respostas estão corretas?
Martin Ba

Para as reações que você estava procurando, simplesmente passeie junto ao rastreador de bug do sol ... por exemplo, aqui , aqui e aqui . Leia os e sentir a raiva indignada :)
Básico

Respostas:


23

Você está errado. O tamanho do heap das JVMs não é fixo, apenas limitado:

  • -Xmx define o tamanho máximo da memória de heap
  • -Xms define o tamanho mínimo da memória de heap

A definição de um limite superior é necessária por vários motivos. Primeiro, ele diz ao coletor de lixo quando entrar em ação. Segundo, evita que a JVM obstrua toda a máquina consumindo muita memória. O tamanho mínimo de heap é provavelmente útil para reservar a quantidade de memória que o programa precisa, pelo menos, para impedir que ela fique sem memória (porque outros processos consomem muito).


9
sem o mínimo é o padrão para evitar um arranque lento quando muito precisa alocar resultando em aumentos heap repetidas, paginação vai lidar com a RAM física se esgotando
catraca aberração

@ratchetfreak essa foi a minha segunda suposição ;-)
user281377

@ user281377 Se for esse o caso, como o C # pode funcionar perfeitamente sem um tamanho máximo de memória heap?
Cmdome #

cmorse: Só posso adivinhar. Talvez o Java seja direcionado a servidores grandes, onde muitos aplicativos compartilham recursos e limites estritamente impostos são desejáveis, enquanto o .net é feito para PCs e servidores menores e mais dedicados.
user281377

@ user281377: Os aplicativos Java que usei que ficam sem espaço de heap geralmente lidam com muito mal, geralmente apenas travando ou sendo muito desgastados a partir de então. E o ASP.net roda em servidores grandes e pequenos muito bem. O que realmente não entendo é por que, por padrão, o Java impõe esse limite. Eu adoraria ouvir o raciocínio por trás da decisão deles ... Tenho certeza de que eles tinham um bom motivo.
Cmorse

6

Imagino que a resposta tenha algo a ver com a herança do Java. Ele foi originalmente projetado como uma linguagem para ser usada em sistemas embarcados, onde obviamente os recursos são limitados e você não deseja que os processos consumam o que estiver disponível. Também ajuda na administração do sistema, pois facilita o provisionamento de recursos em um servidor se você pode definir limites de recursos. Vejo que as JVMs mais recentes parecem usar vários heaps não contínuos, embora, é claro, tudo apareça como um heap único para o seu código.

(FWIW, você tinha que especificar os requisitos de memória de um programa nas versões pré-Darwin MacOS da Apple [até o System 7 de qualquer maneira, que foi o último que eu usei], que estava nos anos 80.)


1
+1 - por ser (1) a única resposta que realmente abordou a questão e (2) por ser plausível.
Kdgregory 07/12/12

2

Você precisa fornecer ao GC algum mecanismo para informar quando executar, ou seu programa preencherá todo o espaço da memória virtual. Existem muitas maneiras alternativas de acionar a GC: tempo decorrido, número de alocações, número de atribuições, provavelmente outras que não consigo pensar agora. Na IMO, nenhum deles é tão bom quanto simplesmente definir um limite de memória e executar o GC quando o espaço alocado atingir esse limite.

A chave é definir os limites corretos . Considero -ms"essa é a quantidade de memória que meu aplicativo precisa" e -mx"nunca deve exceder essa quantidade". Em uma implantação de produção, os dois devem ser próximos, se não iguais, e devem se basear em requisitos reais e medidos.

Suas preocupações com a memória virtual "desperdiçada" são equivocadas: é virtual, é (quase) gratuita. Sim, atribuir uma quantidade muito grande ao heap significa que você não pode iniciar tantos threads ou carregar quantos arquivos mapeados na memória. Mas isso faz parte do design do aplicativo: você tem recursos escassos, precisa particioná-los de uma maneira que permita a execução do aplicativo. Em um heap "estilo C", que se expandirá até você atingir o topo da memória, o problema básico é o mesmo, você só precisa pensar nisso até estar com problemas.

A única coisa que um grande heap pode "desperdiçar" é o espaço de troca, porque todos os segmentos graváveis ​​exigem um compromisso de troca. Mas isso faz parte do design do sistema: se você deseja ter muitas JVMs em execução na mesma caixa, aumente sua troca ou diminua a atribuição de heap. Se eles começarem a debulhar, você está tentando fazer muito com o sistema; compre mais memória (e se você ainda estiver executando um processador de 32 bits, compre uma nova caixa).


1
Mas o limite para a execução do GC não tem nada a ver com um limite fixo que o uso total de memória pelo programa não pode exceder. (Na verdade, provavelmente não deveria; se você tem um monte grande e só consegue GC quando está cheio, você necessariamente tem períodos raros, mas longos de GC). Consulte coleta de lixo geracional.
Ben

@ Ben - sim, você está certo. E minha segunda frase aponta que existem alternativas. No entanto, não concordo que um heap de tamanho fixo seja a maneira errada de gerenciar o GC no caso geral. Uma JVM ajustada corretamente usa o tamanho de heap "correto"; na minha experiência, longas pausas no GC acontecem quando a JVM não foi ajustada corretamente. E geralmente o tamanho "correto" da pilha é muito menor do que você imagina.
kdgregory

-2

Como diz user281377, você especifica apenas um limite superior de quanta memória seu processo pode consumir. Obviamente, o aplicativo em si só pegará o espaço necessário.

Se deve existir um limite superior padrão ou não, é outra questão, tanto a favor quanto a contra.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.