JVM常用命令
命令 | 解释 |
---|---|
jps -l | 查询系统内所有的HotSpot虚拟机进程 |
jstat -gcutil [pic] 1000 | 查看垃圾回收堆的行为统计,指定每一秒打印一次 |
jmap -dump:live,format=b,file=dump.dprof [pid] | 将java堆中存活的对象信息转储到dump文件,可以使用MAT进行分析 也可以可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候,自动生成dump文件 |
jstack [pid] grep [threadId] -A 10 | 查看线程栈情况 threadId 使用 printf "%x" [threadId] 命令转换16进制 |
常用的监控工具
命令/工具 | 说明 |
---|---|
top -Hp [pid] | 监控进程CPU 使用率、内存使用率以及系统负载等信息 |
arthas | alibaba开源JVM监控工具 |
常用JVM参数(jdk1.8)
参数 | 例如 | 描述 |
---|---|---|
-Xms | -Xms512m | 设置JVM的初始堆大小。这是Java进程启动时分配的堆内存大小。 |
-Xmx | -Xmx1024m | 设置JVM的最大堆大小。这是Java进程允许分配的堆内存的最大限制。 |
-Xmn | -Xmn256m | 设置新生代堆内存的初始大小。默认情况下,新生代的大小与整个堆内存的1/3相等 |
-Xss | -Xss1m | 每个线程的栈大小。默认值通常取决于操作系统,但大多数情况下,栈的大小都会比512k要大 |
-XX:NewRatio | -XX:NewRatio=2 | 设置老年代与新生代的内存比例。默认情况下,老年代和新生代的比例是2:1,即老年代占整个堆内存的2/3,新生代占1/3 |
-XX:SurvivorRatio | -XX:SurvivorRatio=8 | 设置Eden区与Survivor区的内存比例。默认情况下,Eden区和Survivor区的比例是8:1,即Eden区占整个新生代内存的8/9,而两个Survivor区共占1/9 |
-XX:MaxTenuringThreshold | -XX:MaxTenuringThreshold=15 | 这个参数决定了对象在新生代中经过多少次垃圾回收后会被晋升到老年代。默认值通常是15次 |
-XX:MaxDirectMemorySize | -XX:MaxDirectMemorySize=512m | 直接内存。报java.lang.OutOfMemoryError: Direct buffer memory 异常可以上调这个值 |
-XX:+HeapDumpOnOutOfMemoryError | 当发生内存溢出错误时,生成堆转储文件(Heap Dump)。这可以帮助分析内存溢出问题。 | |
-XX:HeapDumpPath=./hdpserver_oom.hprof | 指定堆转储文件的路径和文件名。在此示例中,堆转储文件将被命名为 hdpserver_oom.hprof 并保存在当前工作目录中。 | |
-XX:+PrintGCDetails | 打印垃圾回收(GC)的详细信息,包括每次GC的执行时间、回收的内存等。 | |
-XX:+PrintGCDateStamps | 打印垃圾回收(GC)的日期时间戳,显示每次GC发生的时间。 | |
-Xloggc:./gc-recognition.log | 将GC日志输出到文件。在此示例中,GC日志将被写入名为 gc-recognition.log 的文件中。 |
注意:-Xms
和-Xmx
一般设置一样的大小,从而减少堆内存调整、内存抖动,增加稳定性
一般情况下不建议调整Eden区和Survivo相关参数,包括比例、年龄等等
常用输出gc日志参数:-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./gc-recognition.log
使用GCeasy进行在线分析,查看堆、回收情况等等,进行调优
常见的JVM调优场景
1.CPU经常100% 问题定位
问题分析:CPU高一定是某个进程长期占用了CPU资源。
1、所以先需要找出哪个进程占用CPU高
top 列出系统各个进程的资源占用情况
2、然后根据找到对应进程里哪个线程占用CPU高
top -Hp [进程ID] 列出对应进程里面的线程占用资源情况
3、找到对应线程ID后,再打印出对应线程的堆栈信息
printf “%x\n” [threadId] 把线程ID转换为16进制。
jstack [pid] grep [threadId] -A 10 打印出线程信息。
4、最后根据线程的堆栈信息定位到具体业务方法,从代码逻辑中找到问题所在。
查看是否有线程长时间的waiting 或blocked
如果线程长期处于waiting状态下, 关注waiting on xx,说明线程在等待这把锁,然后根据锁的地址找到持有锁的线程。
线程状态解释
状态 | 描述 |
---|---|
NEW | 当你创建一个线程,但还没让它开始工作时,它就是在新建状态。就像给一个工人一件任务,但他还没开始做。 |
RUNNABLE | 当你让这个线程开始工作,它进入可运行状态。这意味着它已经准备好干活了,但是可能还没得到真正的工作机会,就好像一个准备好的运动员等待跑步比赛的开始。 |
RUNNING | 当线程真正开始在 CPU 上执行任务时,它进入运行状态。这就像运动员实际在赛道上跑步一样。 |
BLOCKED | 如果一个线程需要等待一些东西,比如等待其他线程释放一个锁,它就会进入阻塞状态,暂时停止工作。这就像你在排队等待,别的人在使用你需要的东西。 |
WAITING | 有时候线程需要等待特定的信号,比如等待其他线程告诉它可以继续工作了,这时它就会进入等待状态。就好像你在朋友家等他们准备好玩游戏一样。 |
TIMED_WAITING | 类似等待状态,但有时间限制,比如等待一段时间后自动继续工作。就好像你设定一个定时器,过一段时间后再去做其他事情。 |
TERMINATED | 当线程完成了它的任务,或者因为出错而结束时,它进入终止状态。就像一个任务完成的工人下班回家了。 |
2.死锁
1、jps查看java进程
jps -l
2、jstack查看死锁问题
jstack最大的好处就是会把产生死锁的信息(包含是什么线程产生的)输出到最后
jstack <进程ID> > jstack_output.txt
tail -n <行数> jstack_output.txt
使用arthas
查看死锁更加方便,只需要执行thread -b