Prefácio
O título está correto, realmente me fez escrever um bug
!
Quando recebi essa demanda, não senti as menores ondas do coração e fiquei um pouco empolgado. Esta é a minha especialidade; finalmente posso escrever na vertical bug
.
Vamos dar uma olhada nos detalhes primeiro.De fato, o principal motivo é permitir que alguns servidores com baixa carga consumam recursos adicionais, como memória e CPU (para não mencionar o plano de fundo), para que sua carga possa ser aumentada.
Revisão de alocação de memória da JVM
Então, roubei um ônibus e escrevi o código, provavelmente da seguinte maneira:
Depois de escrever, eu estava pensando em uma pergunta: os mem
objetos no código serão reciclados imediatamente após a execução do método? Eu acho que haverá algumas pessoas que pensam que será reciclada depois que o método for executado.
Eu também fiz pesquisas e perguntei a alguns amigos; de fato, alguns deles acham que foi recuperado após a execução do método.
E os fatos? Eu fiz um experimento.
Usei os seguintes parâmetros de inicialização para iniciar o aplicativo agora.
java -Djava.rmi.server.hostname = 10.xx.xx.xx -Djava.security.policy
= jstatd.all.policy
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management.jmxremote. ssl = false
-Dcom.sun.management.jmxremote.port = 8888 -Xms4g -Xmx4g -jar bug-0.0.1-SNAPSHOT.jar
Para que eu possa conectar-me remotamente a este aplicativo através da porta JMX para observar as condições de memória e GC.
Se o método for concluído, o mem
objeto será reciclado quando eu alocar 250M
memória; a memória terá uma curva clara e o GC também será executado.
Observe a curva de memória neste momento.
Você descobrirá que há realmente um aumento significativo, mas não foi recuperado imediatamente depois, mas permaneceu nesse nível da água. Ao mesmo tempo, não há resposta do GC à esquerda.
O jstat
mesmo acontece ao visualizar o layout da memória.
Não importa YGC,FGC
, é apenas que a proporção da área do Éden aumentou.Afinal, 250M de memória são alocados.
Como pode ser reciclado?
Eu aloquei dois 250M novamente e observei a curva de memória.
Verifica-se que o terceiro fuso Eden
horário de 250M é atingido, 98.83%
portanto , a Eden
área de recuperação precisa ser gerada quando for alocada novamente YGC
.
Ao mesmo tempo, a curva de memória também caiu.
Todo o processo de conversão é mostrado na figura:
Como a memória heap inicializada é 4G
, a Eden
área calculada provavelmente é a 1092M
memória.
Além Spring
da 20%
memória aproximada consumida pela inicialização do aplicativo e assim por diante, isso resultará na alocação de 250M de memória 3 vezes YGC
.
Vamos rever a questão agora:
mem
Como o objeto não será coletado após a execução do método, quando será coletado?
De fato, lembre-se de uma coisa: o objeto precisa GC
ser coletado quando o coletor de lixo ocorrer ; não importa se o objeto é uma variável local ou global.
Através do experimento anterior, também descobrimos que os objetos que criamos serão recuperados apenas quando o Eden
espaço for insuficiente .YGC
mem
Mas há realmente uma condição oculta aqui: ou seja, esse objeto é uma variável local. Se o objeto for uma variável global, ele ainda não poderá ser reciclado.
Ou seja, costumamos dizer que o objeto está inacessível, de modo que o objeto inacessível GC
será considerado como o objeto a ser reciclado quando ocorrer.
Após muita consideração, por que algumas pessoas pensam que as variáveis locais serão recuperadas após a execução do método?
Eu acho que isso deve ser confuso, de fato, o que é recuperado após a execução do método 栈帧
.
O resultado mais direto é que mem
o objeto não é mais referenciado. Mas, sem citação, isso não significa que será reciclado imediatamente, ou seja, as necessidades acima mencionadas GC
serão recicladas.
Portanto, o algoritmo de análise de acessibilidade usado para os objetos inacessíveis mencionados acima é usado para indicar quais objetos precisam ser reciclados.
Quando o objeto não é referenciado, é considerado inacessível.
Aqui está uma imagem clara:
Após a execução do método, os mem
objetos nele são equivalentes aos da figura e Object 5
, portanto GC
, serão reciclados no momento.
Dar prioridade a objetos na área do Éden
De fato, pode-se ver no exemplo acima que os objetos são preferencialmente alocados na área Eden da nova geração, mas há uma premissa de que o objeto não pode ser muito grande.
O conteúdo relacionado também foi escrito antes:
Objetos grandes vão diretamente para a geração antiga
Os objetos grandes são alocados diretamente para a geração antiga (quanto ao tamanho, ele pode ser configurado através de parâmetros).
Quando aloquei diretamente 1000M de memória, porque a área Eden não pode ser instalada diretamente, ela foi alocada na geração antiga.
Pode-se ver que Eden
quase não há mudanças na área, mas a geração antiga aumentou 37% .De acordo com a memória da geração antiga 2730M
calculada anteriormente, é quase a mesma que 1000M
a memória.
Visualização de memória do Linux
De volta a esse tempo, preciso concluir a demanda: aumentar a memória do servidor e o consumo da CPU.
Felizmente, a própria CPU tem uma certa quantidade de uso e, ao mesmo tempo, cada objeto criado consumirá alguma CPU.
Principalmente a memória, vamos primeiro olhar para a memória antes de iniciar este aplicativo.
Provavelmente usava apenas 3G de memória.
Após iniciar o aplicativo, ele consome apenas 600M de memória.
Para atender à demanda, preciso alocar um pouco de memória, mas aqui preciso prestar atenção.
A memória não pode ser alocada o tempo todo, o que fará com que a carga da CPU seja muito alta e, ao mesmo tempo, a memória não será particularmente ocupada devido à reciclagem do GC.
Portanto, preciso de uma pequena quantidade de alocação para que a maioria dos objetos da nova geração precise ser mantida em 80% ou 90% para não ser reciclada.
Ao mesmo tempo, alguns objetos grandes precisam ser alocados para a geração antiga, e o uso da geração antiga deve ser mantido em 80% ou 90%.
Dessa maneira, a memória heap 4G pode ser usada ao máximo.
Então eu fiz o seguinte:
- Primeiro, aloque alguns objetos pequenos na nova geração (800M) para manter a nova geração em 90%
- Em seguida, foi alocado
老年代内 *(100%-已使用的28%);也就是 2730*60%=1638M
para que a geração antiga também estivesse em torno de 90%.
O efeito é como acima.
O mais importante é GC
que isso não aconteceu uma vez e alcançou meu objetivo.
Eventualmente, a memória consumiu cerca de 3,5G.
Sumário
Embora desta vez a demanda seja relativamente estranha, JVM
ainda não é tão fácil controlar a alocação de memória com precisão .
Você precisa ter um certo entendimento de seu layout de memória e reciclagem. O processo de escrever esse bug realmente aprofundou a impressão. Se for útil para você, não seja mesquinho com seus gostos e compartilhamento.