上篇文章主要分析了一下JVM和GC算法等理论,这篇主要就是实战。
GC的目标
1.1、GC优化是必要的吗
事实上GC优化对Java基础服务来说在有些场合是可以省去的,但前提是这些正在运行的Java系统,必须包含以下参数或行为:
- 内存大小已经通过-Xms和-Xmx参数指定过
- 运行在server模式下(使用-server参数)
- 系统中没有残留超时日志之类的错误日志
我们GC优化的最主要的目的:
- 减少Full GC的执行时间
- 将进入老年代的对象数量降到最低
1.2、GC调优调的是什么
我们是通过jvm的配置参数,来达到调优的目的。
类型 | 参数 | 描述 |
---|---|---|
堆内存大小 | -Xms | 启动JVM时堆内存的大小 |
-Xmx | 堆内存最大限制 | |
新生代空间大小 | -XX:NewRatio | 新生代和老年代的内存比 |
-XX:NewSize | 新生代内存大小 | |
-XX:SurvivorRatio | Eden区和Survivor区的内存比 |
1.3、GC调优的结果
我们主要关注以下结果:
单次Full GC运行时间
单次Minor GC运行时间
Full GC运行间隔
Minor GC运行间隔
整个Full GC的时间
整个Minor GC的运行时间
整个GC的运行时间
Full GC的执行次数
Minor GC的执行次数
1.4、常用命令
打开GC日志记录:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:<filename>
1.5、常用工具
GCViewer
java -jar gcviewer_1.3.4.jar gc.log
将分析结果输出到文件:
java -jar gcviewer_1.3.4.jar gc.log summary.csv chart.png
JConsole
1.6、常见问题定位过程
频繁GC问题或内存溢出问题
一、使用jps查看线程ID
二、使用jstat -gc 3331 250 20 查看gc情况,一般比较关注PERM区的情况,查看GC的增长情况。
三、使用jstat -gccause:额外输出上次GC原因
四、使用jmap -dump:format=b,file=heapDump 3331生成堆转储文件
五、使用jhat或者可视化工具(Eclipse Memory Analyzer 、IBM HeapAnalyzer)分析堆情况。
六、结合代码解决内存溢出或泄露问题。
死锁问题
一、使用jps查看线程ID
二、使用jstack 3331:查看线程情况
此外,如果GC执行时间满足下列所有条件,就没有必要进行GC优化了:
+ Minor GC执行非常迅速(50ms以内)
+ Minor GC没有频繁执行(大约10s执行一次)
+ Full GC执行非常迅速(1s以内)
+ Full GC没有频繁执行(大约10min执行一次)
GC优化的过程:
1.监控GC状态
你需要监控GC从而检查系统中运行的GC的各种状态
2.分析监控结果后决定是否需要优化GC
在检查GC状态后,你需要分析监控结构并决定是否需要进行GC优化。如果分析结果显示运行GC的时间只有0.1-0.3秒,那么就不需要把时间浪费在GC优化上,但如果运行GC的时间达到1-3秒,甚至大于10秒,那么GC优化将是很有必要的。在进行GC优化之前,你需要考虑为什么你需要分配这么大的内存空间,如果你分配了1GB或2GB大小的内存并且出现了OutOfMemoryError,那你就应该执行堆转储(heap dump)来消除导致异常的原因。
3.设置GC类型/内存大小
如果你决定要进行GC优化,那么你需要选择一个GC类型并且为它设置内存大小。此时如果你有多个服务器,请如上文提到的那样,在每台机器上设置不同的GC参数并分析它们的区别。
4.分析结果
在设置完GC参数后就可以开始收集数据,请在收集至少24小时后再进行结果分析。如果你足够幸运,你可能会找到系统的最佳GC参数。如若不然,你还需要分析输出日志并检查分配的内存,然后需要通过不断调整GC类型/内存大小来找到系统的最佳参数。
5.如果结果令人满意,将参数应用到所有服务器上并结束GC优化
如果GC优化的结果令人满意,就可以将参数应用到所有服务器上,并停止GC优化。