JVM

2/16/2023 JVM

# 1. JVM内存模型

线程独占: 栈,本地方法栈,程序计数器 
线程共享: 堆,方法区

# 2. 启动参数

-Xss:每个线程的栈大小
-Xms:设置堆的初始可用大小,默认物理内存的1/64
-Xmx:设置堆的最大可用大小,默认物理内存的1/4

# 3. 什么时候需要JVM调优

1. 堆内存(老年代)持续上涨达到设置的最大内存值;
2. Full GC 次数频繁;
3. GC 停顿时间过长(超过1秒);
4. 系统吞吐量与响应性能不高或下降

# 4. JVM调优的步骤

1. 分析系统系统运行情况:分析GC日志及dump文件,判断是否需要优化,确定瓶颈问题点;
2. 依次确定调优内存、延迟、吞吐量等指标
3. 对比观察调优前后的差异;
4. 不断的分析和调整,直到找到合适的JVM参数配置;

# 5. 调整内存大小

现象:垃圾收集频率非常频繁。
如果内存太小,就会导致频繁的需要进行垃圾收集才能释放出足够的空间来创建新的对象,所以增加堆内存大小的效果是非常显而易见的。
注意:如果垃圾收集次数非常频繁,但是每次能回收的对象非常少,那么这个时候并非内存太小,而可能是内存泄露导致对象无法回收,从而造成频繁GC。
-Xms:设置堆的初始可用大小,默认物理内存的1/64
-Xmx:设置堆的最大可用大小,默认物理内存的1/4
-Xmn512m 新生代内存配置

# 6. 设置符合预期的停顿时间

现象:程序间接性的卡顿
如果没有确切的停顿时间设定,垃圾收集器以吞吐量为主,那么垃圾收集时间就会不稳定。
-XX:MaxGCPauseMillis GC停顿时间

# 7. 调整内存区域大小比率

现象:某一个区域的GC频繁,其他都正常。
如果对应区域空间不足,导致需要频繁GC来释放空间。
-XX:SurvivorRatio=6 survivor区和Eden区大小比率
-XX:NewRatio=4 表示新生代:老年代

# 8. 例子

a. 现象:网站流量浏览量暴增后,网站反应页面响很慢
b. 定位:为了确认推测的正确性,在线上通过jstat -gc 指令 
    看到JVM进行GC次数频率非常高,GC所占用的时间非常长,
    所以基本推断就是因为GC频率非常高,所以导致业务线程经常停顿
c. 解决方案:因为网页访问量很高,所以对象创建速度非常快,导致堆内存容易填满从而频繁GC.
    所以这里问题在于新生代内存太小,所以这里可以增加JVM内存就行了,所以初步从原来的2G内存增加到16G内存。
d. 第二个现象:不定期的会间断性的卡顿,而且单次卡顿的时间要比之前要长很多。
e. jstat -gc 指令 查看到,FGC次数并不是很高,但是花费在FGC上的时间是非常高的
f. 定位:JVM默认使用的是PS+PO的组合,标记和收集阶段都是STW。
g. 解决方案:一方面设置最大停顿时间,XX:MaxGCPauseMillis=200。JVM可用内存6G以上,另一方面可以采用G1处理器。

# 9. CPU经常100%

一般为线程锁竞争,或频繁full GC
a. top命令列出系统各个进程的资源占用情况
b. top -Hp 找出对应的线程
c. printf "%x\n" PID 把线程ID转换为16进制
d. jstack PID 打印出进程的所有线程信息,查看是否在waiting,关注等待什么锁,结合代码找到原因。
如果是full GC则
a. jstat -gc 查看gc情况
b. jmap -dump 把堆信息dump下来分析什么对象

# 10. 内存飚高问题定位

内存飚高如果是发生在java进程上,一般是因为创建了大量对象所导致,持续飚高说明垃圾回收跟不上对象创建的速度,或者内存泄露导致对象无法回收。
更新时间: 2/16/2023, 5:28:01 PM