prefácio
Programadores Java entrevistar JVM é quase mais difícil do que fazer perguntas. Perguntas como monitoramento de JVM, OOM online e carga de CPU 100% são frequentemente feitas. Embora possa não ser nossa vez de lidar com problemas online em empresas, seja para entrevistas ou Para lidar com o desenvolvimento, é necessário entender o tratamento de problemas on-line da JVM.
Relativamente falando, seja para resolver o problema de falha ou lidar com gargalos de performance, a ideia geral é mais ou menos a mesma, ou seja: analisar dados (logs), analisar e solucionar problemas, localizar problemas e resolver problemas. dados de execução ou logs Se não conseguirmos, não temos como localizar o problema.
Felizmente, Java enviou ferramentas de monitoramento JVM e instruções relacionadas para nos ajudar a obter dados relacionados a JVM para nos ajudar a solucionar problemas.
instalação de ferramentas
Sabemos apenas que existe uma JVM, mas sua operação parece invisível para nós, então precisamos de ferramentas para monitorar seu status em tempo real, assim como o monitor de desempenho do Windows, o JDK também possui suas próprias ferramentas de visualização. Java fornece 2 ferramentas de monitoramento :
-
D:\opensource\jdk1.8\bin\jconsole.exe
-
D:\opensource\jdk1.8\bin\jvisualvm.exe
jconsole
Digite jconsole através da linha de comando cmd e a seguinte interface aparecerá
: Selecione java para entrar e você poderá ver o status da memória, status do carregamento da classe, status do thread etc.
Jvisualvm
Executamos cmd, inserimos jvisualvm e iniciamos o Java VisualVM.
O menu local à esquerda é o processo java. Depois de selecionar um processo, você pode ver o heap, o status de carregamento da classe e o status pronto à direita.
jvisualvm instala o plug-in GC
O jvisualvm integrado não monitora a função de coleta de lixo do GC, precisamos instalar plug-ins adicionais:
Abra Ferramentas -> Plugins -> Selecione a página "Plugins Disponíveis": Instalamos um Visual GC aqui, para que possamos ver a recuperação de memória e o status de cada geração. Após marcar, clique em Instalar, que é o próximo e regular concordar com o acordo, etc. A rede não é muito estável, às vezes pode demorar mais algumas tentativas. Você pode modificar o endereço do centro de plug-in nas configurações:
Modifique o endereço de acordo com as seguintes etapas: Encontre o centro de plug-in
http://visualvm.github.io/pluginscenters.html
Encontre a versão correspondente do JDK:
http://visualvm.github.io/archive/uc/8u40/updates.htmlCopiar
endereço do plug-in:
Instale o plug-in:
Em seguida, encontre o Visual GC nos plug-ins disponíveis
Após a conclusão da instalação, fechamos a página de monitoramento atual, abrimos novamente e você pode ver que há uma página adicional do Visual GC atrás do Profiler.
Aqui podemos ver o tempo de atividade do JIT, o tempo de atividade de carregamento da classe, o tempo de atividade do GC e a situação de cada geração.
Deve-se observar que a versão JDK usada pelo curso atual é 1.8, que ainda vem com o VisualVM. A versão a partir de 1.9 não vem com ele e precisa ser baixada adicionalmente. O endereço do github para download é:
https://visualvm.github.io/download.html
Além disso, se a ferramenta de desenvolvimento usa Intellij IDEA, você pode baixar um plug-in, VisualVM Launcher, e ir diretamente para a página acima por meio da inicialização do plug-in, sem procurar seu próprio projeto na entrada à esquerda.
Claro, existem outras ferramentas, mas este será o principal desenvolvimento da ferramenta de tratamento de falhas tudo-em-um no futuro previsível. Portanto, usaremos esta ferramenta para analisar nossa operação JVM e otimizá-la posteriormente. Para otimizar, nós também precisa Ter uma compreensão mais aprofundada da composição da JVM
Comando de monitoramento da JVM
No ambiente de produção, geralmente encontramos vários problemas de desempenho estranhos. Podemos usar os comandos de monitoramento JVM fornecidos pelo Java para obter efeitos de monitoramento e visualização. Os comandos relacionados são os seguintes
nome | efeito principal |
---|---|
jps | Exibir processos Java em execução |
jstack | imprimir instantâneo do segmento |
jmap | Exportar arquivo de imagem de memória heap |
ficar de pé | Ver estatísticas jvm |
jinfo | Visualize e modifique os parâmetros de configuração do jvm em tempo real |
Jhat | Usado para analisar arquivos de despejo de pilha |
jps ver progresso
jps pode listar os processos Java em execução e exibir o nome da classe principal (Main Class, a classe onde a função main() está localizada) da máquina virtual e o id do processo, e os parâmetros podem ser visualizados através de jps -help
opções | efeito |
---|---|
-q | Apenas imprima o ID do processo |
-m | Saída dos parâmetros passados para a função principal da classe principal |
-eu | Emita o nome completo da classe principal, se o processo executar o pacote Jar, emita o nome do pacote jar |
-v | O parâmetro jvm especificado quando o programa inicia |
Apresentação do caso:
jstack: imprime instantâneo do thread
Geralmente, no ambiente de produção, se ocorrerem problemas como pausa de longo prazo, travamento, impasse e tempo de solicitação longo, você pode analisar e localizar o problema imprimindo instantâneos de encadeamento. O seguinte é um código de impasse:
public class DeadlockExample {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
synchronized (lock1) {
System.out.println("Thread 1 acquired lock 1");
try {
Thread.sleep(1000);
synchronized (lock2) {
System.out.println("Thread 1 acquired lock 2");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread() {
public void run() {
synchronized (lock2) {
System.out.println("Thread 2 acquired lock 2");
try {
Thread.sleep(1000);
synchronized (lock1) {
System.out.println("Thread 2 acquired lock 1");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Em seguida, tentamos jps -l para visualizar o processo primeiro
, obter a identificação do processo e usar jstack para visualizar cada instantâneo de encadeamento: jstack 27928
No instantâneo de encadeamento, você pode ver todos os encadeamentos no processo atual. Entre eles estão os threads do nosso código e o estado está bloqueado. Ao mesmo tempo, ele solicita encontrado 1 impasse para encontrar um impasse e fornece o local onde ocorreu o impasse.
jmap: exportar instantâneo da pilha
A execução jmap -histo pid
pode imprimir o número de instâncias e uso de memória de cada classe no heap atual, como segue, nome da classe é o nome da classe de cada classe ([B é o tipo de byte, [C é o tipo de char, [I é o tipo int) , bytes É o tamanho da memória ocupada por todos os exemplos desta classe, e instances é o número de instâncias desta classe:
execute jmap -dump para despejar o instantâneo da memória heap em um arquivo especificado, como executar
jmap -dump:format=b,file=/data/jvm/dumpfile_jmap.hprof PID , você pode despejar o instantâneo da memória heap atual no arquivo dumpfile_jmap.hprof e, em seguida, analisar o instantâneo da memória.
Geralmente configuramos o ambiente de produção para que a máquina virtual gere automaticamente um arquivo de despejo após a ocorrência de uma exceção OOM
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users
Por exemplo, há um loop infinito de código e executá-lo por um determinado período de tempo causará estouro de memória
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
int i = 0;
while(true){
arrayList.add(new Main());
System.out.println(i++);
}
}
}
Para o efeito óbvio, definimos o heap para ser menor e, em seguida, definimos HeapDumpOnOutOfMemoryError em
-Xms2m
-Xmx2m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:\
Defina os parâmetros de VM acima para o aplicativo atual e o efeito da execução do código é o seguinte
Em seguida, encontramos o arquivo de instantâneo de heap hprof, que pode ser carregado e analisado por meio da ferramenta jvisualvm.
Agora, o arquivo de instantâneo, o tipo de arquivo precisa ser selecionado como um instantâneo de heap.
Após o carregamento, você pode ver diretamente a mensagem de erro de estouro de memória
Depois de clicar no nome do encadeamento principal, você pode localizar diretamente o local do estouro de memória
jstat: monitora as informações da máquina virtual
jstat -gc pid 500 10
: pid é o ID do encadeamento e imprime o status do heap Java a cada 500 milissegundos (capacidade, capacidade de uso, tempo gc, etc. de cada área). Imprimindo 10 vezes jstat também pode monitorar o tamanho da memória de cada área e monitorar a classe carregando informações de outros ângulos.
Especificamente, você pode pesquisar no Google o uso detalhado de jstat. A seguir está a comparação do resultado
S0C: o tamanho da primeira área sobrevivente
S1C: o tamanho da segunda área sobrevivente
S0U: o tamanho usado da primeira área sobrevivente
S1U: o tamanho usado da segunda área sobrevivente
EC: o tamanho da área Eden
EU: o uso da área do Éden Tamanho
OC: Tamanho da Geração Antiga
OU: Tamanho do Uso da Geração Antiga
MC: Tamanho da Área do Método
MU: Tamanho do Uso da Área do Método
CCSC: Tamanho do Espaço de Classe Comprimido CCSU
: Tamanho do Uso do Espaço de Classe Comprimido
YGC: Tempos de Coleta de Lixo da Geração Jovem
YGCT: Jovem Tempo de Consumo de Coleta de Lixo de Geração
FGC: Tempos de coleta de lixo de geração antiga
FGCT: Tempo de coleta de lixo de geração antiga
GCT: Tempo total de coleta de lixo
Unidade: KB
jinfo: ver parâmetros do processo
jinfo(Configuration Info for Java) Verifique os parâmetros de configuração da máquina virtual e também pode ser usado para ajustar os parâmetros de configuração da máquina virtual.
Em muitos casos, os aplicativos Java não especificam todos os parâmetros da máquina virtual Java. Neste momento, os desenvolvedores podem não saber o valor padrão de um parâmetro específico da máquina virtual Java. Nesse caso, pode ser necessário obter o valor padrão de um parâmetro consultando a documentação. Este processo de pesquisa pode ser muito difícil. Mas com a ferramenta jinfo, os desenvolvedores podem encontrar facilmente o valor atual dos parâmetros da máquina virtual Java.
jinfo pode não apenas visualizar o valor real de um determinado parâmetro da máquina virtual Java em tempo de execução, mas também modificar alguns parâmetros em tempo de execução e fazê-los entrar em vigor imediatamente. No entanto, nem todos os parâmetros suportam modificação dinâmica. Os parâmetros só podem ser modificados em tempo real com o sinalizador marcado gerenciável. Na verdade, essa capacidade de modificação é extremamente limitada.
Sinalizadores de VM:
Sinalizadores de VM não padrão: -XX:CICompilerCount=12 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=null -XX:InitialHeapSize=2097152 -XX:MaxHeapSize=209715200 -XX:MaxNewSize=69730304 -XX:Min HeapDeltaBytes = 524288 -XX:OldSize=524288 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Linha de comando: -Xms2m -Xmx200m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d: \ -javaagent:D:\Arquivos de Programas\JetBrains\IntelliJ IDEA 2022.2.1\lib\idea_rt.jar=54817:D:\Arquivos de Programas\JetBrains\IntelliJ IDEA 2022.2.1\bin -Dfile.encoding=UTF-8
Por meio de jinfo -flags pid: visualize o valor do parâmetro ao qual foi atribuído um valor
jinfo pode não apenas visualizar o valor real de um determinado parâmetro da máquina virtual Java em tempo de execução, mas também modificar alguns parâmetros em tempo de execução e fazê-los entrar em vigor imediatamente. No entanto, nem todos os parâmetros suportam modificação dinâmica. Os parâmetros só podem ser modificados em tempo real com o sinalizador marcado gerenciável. Você pode usar o comando java -XX:+PrintFlagsInitial | grep manageable para visualizar o
formato de modificação gerenciável da seguinte forma:
- Para modificação do tipo booleano: jinfo -flag ±parameter pid
- Para tipos não booleanos: jinfo -flag nome do parâmetro=valor do parâmetro pid
Por exemplo, modifique e imprima a demonstração do log do GC da seguinte forma: jinfo -flag +PrintGCDetails PID
//查看进程
C:\Users\Administrator>jps -l
20924 org.example.Main
...
//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924
//增加jvm参数:打印GC详情
C:\Users\Administrator>jinfo -flag +PrintGCDetails 20924
//查看是否设置PrintGCDetails参数配置
C:\Users\Administrator>jinfo -flag PrintGCDetails 20924
-XX:+PrintGCDetails
...
Resolva o problema on-line CPU100%
Geralmente, 100% da CPU é basicamente causada por um loop infinito de código. A ideia central da investigação é encontrar o servidor correspondente, localizar quais códigos em qual thread de qual processo causou o problema e apresentar brevemente os exemplos de códigos anormais naquele momento.
A primeira etapa é encontrar o processo que consome mais CPU: use top -c para visualizar o processo e, em seguida, insira o P grande para classificar de acordo com o uso da CPU.
O segundo passo é encontrar a thread que mais consome CPU no processo: encontre o processo com maior CPU, encontre o ID do processo (PID), encontre a thread correspondente a este processo através do comando top -Hp PID, e então digite o grande P para classificar de acordo com o uso da CPU.
Obtenha o primeiro PID é o ID do encadeamento que consome mais tempo e, em seguida, use printf "%x\n" PID para converter o PID de decimal para hexadecimal (o motivo para converter para hexadecimal é porque na pilha, O id do encadeamento é expresso em hexadecimal.)
[root@VM-4-2-centos ~]# printf "%x\n" 13759
35bf
Em seguida, precisamos usar o jstack para imprimir as informações da pilha do processo e, em seguida, usar o grep para visualizar as coisas relacionadas ao thread correspondentes. ID do processo jstack | grep "ID do thread" -C5 --color
jstack 30979 | grep "35bf" -C5 --color
Neste momento, você pode imprimir o código e, em seguida, combinar o nid do instantâneo do thread impresso, localizar qual thread é demorado e, ao mesmo tempo, localizar rapidamente o código, você pode ver qual método em qual classe causou a CPU 100% do motivo.
Monitoramento remoto
Se você não estiver muito familiarizado com comandos, é difícil usar comandos para monitorar a JVM. JVisualvm fornece a função remota jmx. O padrão é fornecer serviços RMI por meio do endereço IP do localhost, o que exige que configuremos os parâmetros da JVM remotamente para permitir conexões remotas
-Xms256m
-Xmx512m
-Xss256m
-XX:PermSize=512m
-XX:MaxPermSize=1024m
-Dcom.sun.management.jmxremote
-Djava.rmi.server.hostname=服务器IP
#远程服务的端口:
-Dcom.sun.management.jmxremote.port=9015
#客户端 rmi通信端口
-Dcom.sun.management.jmxremote.rmi.port=9015
#关闭ssl功能
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
Em seguida, adicionamos o host remoto no Jvisualvm local
e, em seguida, adicionamos a conexão remota jmx
para definir os parâmetros de conexão remota e cancelar a conexão ssl.
O artigo acabou. Se for útil para você, faça uma boa revisão. Seu encorajamento é minha maior motivação