目录
开篇介绍:
生产环境中直接排查JVM的话,使用适当的虚拟机监控和分析的工具可以加快我们分析数据、定位解决问题的速度。本博文重点介绍Jconsole和VisualVM工具的使用。
一、本地Jconsole远程连接阿里云服务
先配置环境变量:
export JAVA_OPTS='-Djava.rmi.server.hostname=112.74.xxxx.xxx -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=7777 -Dcom.sun.management.jmxremote.rmi.port=7777 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false'
配置介绍:
hostname 阿里云公网IP
jmxremote.port和rmi.port 连接时候所使用的端口号,自定义。阿里云的话记得在安全组中开放这个。
配置好后,source /etc/profile 立即让配置生效
然后启动jar包
在本地cmd中输入jconsole,在弹出来的控制台上填写远程进程信息:
二、先看本地Jconsole内存监控验证
启动程序main,并连接上服务
package com.wnn.jvm; import java.util.ArrayList; import java.util.List; public class JconsoleMemory { public byte[] b1 = new byte[1024*512]; public static void main(String[] args) throws InterruptedException { System.out.println("main thread start"); Thread.sleep(10000); allocate(10000); } private static void allocate(int n) { List<JconsoleMemory> jconsoleMemoryList = new ArrayList<>(); for (int i = 0; i < n; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } jconsoleMemoryList.add(new JconsoleMemory()); } } }
看看堆内存的使用 随着时间加大,堆使用量持续增长,因为循环过程中一直在堆中创建对象在方法体中有引用,无法回收,一直增大
老年代也在不断增长
Eden区 占满的时候会发生复制 ,所以增长会呈现折现的方式
持续增加下,就发生Java heap space
java.lang.OutOfMemoryError: Java heap space 堆溢出 错误的原因, 很多时候, 就类似于将 XXL 号的对象,往 S 号的 Java heap space 里面塞
三、再看本地Jconsole线程验证
线程状态waiting、timeWaiting 死锁情况的模拟
package com.wnn.jvm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.annotation.PostConstruct;
@SpringBootApplication
@EnableAutoConfiguration
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private static Object lock1 = new Object();
private static Object lock2 = new Object();
class WaitCondition{
boolean isOpen = false;
public void setOpen(){
isOpen = true;
}
}
WaitCondition waitCondition = new WaitCondition();
/**
* 在springboot项目启动的时候所执行的方法
* 在bean加载完但用户线程进来之前执行的方法
*/
@PostConstruct
public void deadLock(){
new Thread(()->{
synchronized (lock1){
try {
System.out.println(Thread.currentThread().getName()+"得到Lock1");
Thread.sleep(3000L);
}catch (Exception e){
e.printStackTrace();
}
synchronized (lock2){
System.out.println(Thread.currentThread().getName()+"得到Lock2");
}
}
},"线程1").start();
new Thread(()->{
synchronized (lock2){
try {
System.out.println(Thread.currentThread().getName()+"得到Lock2");
Thread.sleep(3000L);
}catch (Exception e){
e.printStackTrace();
}
synchronized (lock1){
System.out.println(Thread.currentThread().getName()+"得到Lock1");
}
}
},"线程2").start();
new Thread(()->{
synchronized (waitCondition){
while (!waitCondition.isOpen){
try {
waitCondition.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"线程3").start();
new Thread(()->{
synchronized (waitCondition){
while (!waitCondition.isOpen){
try {
waitCondition.wait(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"线程4").start();
}
}
线程一,二是死锁的状态
WAITING 一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态:
TIMED_WAITING 一个正在限时等待另一个线程执行一个动作的线程处于这一状态:
Blocked状态与Waiting状态的区别:
WAITING 状态属于主动地显式地申请的阻塞,BLOCKED 则属于被动的阻塞
四、idea -VisualVM插件安装
VisualVM是什么?
VisualVM是一个集成命令行JDK工具和轻量级分析功能的可视化工具
第一步:在IDEA安装VisualVM插件,File-> Setting-> Plugins -> Browers Repositrories 搜索VisualVM Launcher安装并重启IDEA
第二步:点击配置VisualVM executable执行路径
idea右侧上方多了2个按钮:
然后正常启动程序,会发现 有个弹窗出来了
运行段错误代码,然后堆dump下,再看看耗费的实例数:
@PostConstruct
public void visualVM() throws InterruptedException{
Map<Integer,Integer> map = new HashMap<>();
int i = 0;
for (int j = 0; j < 1000; j++) {
Thread.sleep(10000);
while (i<2000000){
i++;
try{
map.put(i,i);
}catch (OutOfMemoryError e){
e.printStackTrace();
break;
}
}
}
}
可以通过这种方式来看实际运行服务中具体是哪个对象占用的实例数很大。