JVM调优
一 JVM调优常用工具和命令
1、可以用来观察GC图形化的工具:1 jconsole远程连接;2 jvisualvm远程连接;3 jprofiler (收费)
2、图形化界面一般用在什么地方? 测试!在压力测试的时候进行监控。
3、已经上线的系统不用图形界面,用cmdine arthas
4、jmap常用命令:
jmap -histo pid | head -20 查找进程中产生的对象的前20名
jmap -dump:format=b,file=文件名称 pid 把内存状态全部存储到文件中(内转储)
5、top常用命令
top 观察是哪些进程导致内存不断增长,占用CPU占用居高不下
top -Hp pid 观察进程中的哪个线程内存和CPU比较高
6、jps 定位java进程
7、jstack pid 定位线程状况
8、jinfo pid 列出进程的详细信息
9、jstat -gc 动态观察GC情况(jstat -gc 500 每500毫秒打印GC情况)
二 OOM问题的定位
首先我们来模拟两种情况下的内存溢出的情况:内存泄漏和线程死锁
1 内存泄漏导致OOM
1.1 模拟编写内存泄漏的代码
package com.study.heap; import java.util.ArrayList; import java.util.List; /** * 测试排查内存溢出的的解决方案 * * @author zhangpba * @date 2021-11-01 */ public class HeapOOM { private int heapObjectNum; public HeapOOM(int heapObjectNum) { this.heapObjectNum = heapObjectNum; } public static void main(String[] args) throws Exception{ List<HeapOOM> list = new ArrayList<>(); // 不断往集合中放入元素,让其内存溢出 int i = 0; while (true){ Thread.sleep(1); System.out.println(i++); list.add(new HeapOOM(i)); } } }
1.2 配置启动参数
在idea中的Edit Configuration的VM Options中添加如下参数
-verbose:gc -Xms20m -Xmn10m -Xmx20m -XX:+PrintGCDetails -XX:SurvivorRatio=8
-verbose:gc:控制台输出GC日志
-Xms:初始内存大小(memory size)
-Xmn:最小内存
-Xmx:最大内存
-XX:+PrintGCDetails:控制台输出GC具体日志信息
-XX:SurvivorRatio=8:Eden区占比,新生代分为Eden区和Survivor区(from/to Survivor),SurvivorRatio参数定义了Eden、Survivor的比例,默认为8,也就是Eden占新生代的8/10
1.3 启动测试代码,观察内存中的变化
启动后代码报异常:java.lang.OutOfMemoryError: Java heap space
本地通过jvisualVM来观察内存中的变化,或者用jmap -heap pid来查看老年代,Eden和s0,s1区域内存占用的情况变化
1.3.1 jvisualVM来观察到的变化:
使用jvisualvm
在mac环境下,如果已经部署了java,那么直接使用jvisualvm命令即可打开界面。
在win环境下,安装好JDK,进入jvisualvm目录,路径:%JAVA_HOME%/bin下面的jvisualvm,双击jvisualvm.exe,弹出页面;
堆中的变化:
在jvisualVM中,很容易看到锁执行的进程号为:10336
1.3.2 用jmap -heap pid查看的情况
1.4 jmap查看最耗内存的对象
jmap命令来查看这个java进程中的对象占用空间的排序:jmap -histo:live pid| more;如果只查看前20名的话,用命令:jmap -histo pid| head -20
分析出我们写的测试类HeapOOM所创建的对象是导致内存溢出的罪魁祸首。然后针对这个类所涉及的代码进行优化(首先需要对JVM参数进行优化)。
1.5.OOM优化
2 线程死锁导致OOM
2.1 模拟编写死锁代码
package com.study.heap; /** * 死锁线程 * * @author zhangpba * @date 2021-11-01 */ public class DeadLock { private Object lockA = new Object(); private Object lockB = new Object(); private void deadLock() { // 定义线程t1,锁定A变量睡眠2秒去抢占B的资源 Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("进入线程t1"); synchronized (lockA) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName()); } } } }, "thread-zhangpba-1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println("进入线程t2"); synchronized (lockB) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockA) { System.out.println("线程名:" + Thread.currentThread().getName()); } } } }, "thread-zhangpba-1"); t1.start(); t2.start(); } public static void main(String[] args) { new DeadLock().deadLock(); } }
2.2 通过visualVM排查线程死锁问题
无死锁
有死锁:会在上面显示红色字体:检测到死锁!生成一个线程 Dump 以获取更多信息
点击线程Dump,可以看到详细死锁信息:
3 排查CPU占用率高的问题
top命令:找到占用cpu最高的进程(java进程可以用jps)
top -Hp pid:查看指定进程中占用CPU最高的线程(按照占用CPU的比例排序)
printf '%x' 线程号:将线程id转化为16进制
jstack pid > a.txt:将进程中的线程信息全部保存到a.txt文件中
去a.txt文件中查找线程,分析线程有什么问题
参考:https://juejin.cn/post/7145383084341821448
根据提供的这些命令和工具,整体的思路可以参考这篇博客:https://blog.csdn.net/weixin_38612401/article/details/123848025
本文来自博客园,作者:zhangpba,转载请注明原文链接:https://www.cnblogs.com/zhangpb/p/15496582.html