JVM性能调优(二)

使用jstack如何发现死锁?

使用jstack观察程序有没有死锁的时候,首先检索文件,看看有没有dead lock这些字眼,
接着就要检测有没有blacked这些字眼

使用MAT分析发现内存泄漏的问题

1.首先调用
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/administrator/james/error.hprof
然后就可以输出一个dump文件
2.Historgam:可以查看到所有的所有的实例,关键的几个属性:objects(个数)
shallow heap(占用的内存大小) Retained Heap(回收某一个对象,包含这个对象,和这个对象关联的下级对象的总大小)
3.dominator_tree:支配树,树形的对象的支配关联
4.说明一下什么情况下会出现内存泄漏?
当一个对象永远可达的时候,且不是程序所希望的,那么就是出现内存泄漏了
5.找到GCROOT
6.总结:内存里面有很多个同一类型的对象,而且都有GCROOT指向它们,且不是程序设计所希望的,那么那就出现内存泄漏

如何实际的去思考学习和排查内存泄漏

1.查看GC日志,做了一次full GC,如果发现前后代大小没有发生什么变化,则说明有很多对象没有被回收掉
2.发现问题后需要去打印出dump日志
3.使用MAT观察
1.占用Retained Heap
2.观察占用的对象有没有GCROOT

实际案例的使用

各个代如何分配大小

各分区的大小对GC的性能影响很大。如何将各分区调整到合适的大小,分析活跃数据的大小是很好的切入点。
活跃数据的大小是指,应用程序稳定运行时长期存活对象在堆中占用的空间大小,也就是Full GC后堆中老年代占用空间的大小。可以通过GC日志中Full GC之后老年代数据大小得出,比较准确的方法是在程序稳定后,多次获取GC数据,通过取平均值的方式计算活跃数据的大小。活跃数据和各分区之间的比例关系如下
总大小 3-4倍,新生代:1-1.5倍 ,老年代:2-3倍

首先你要确认JVM性能优化的目标

高可用,可用性达到几个9。
低延迟,请求必须多少毫秒内完成响应。
高吞吐,每秒完成多少次事务

分析案例一

观看GC日志,如果你发现经常发生major GC然后导致应用的响应时间变长,思考,如果每次major GC后,都回收了很多的内存,说明了很多应该留在新生代就被回收的对象进入了老年代,然后才进行数据回收
解决方案:扩大新生代

分析案例二

观察GC日志,发现在CMS Rmark之前,没有当道一次minor GC导致等待的时间过长
通过案例分析了解到,由于跨代引用的存在,CMS在Remark阶段必须扫描整个堆,同时为了避免扫描时新生代有很多对象,增加了可中断的预清理阶段用来等待Minor GC的发生。只是该阶段有时间限制,如果超时等不到Minor GC,Remark时新生代仍然有很多对象,我们的调优策略是,通过参数强制Remark前进行一次Minor GC,从而降低Remark阶段的时间。

什么时候会触发FULL GC

1.Perm(永久带)空间不足;
2.CMS GC时出现promotion failed和concurrent mode failure(concurrent mode failure发生的原因一般是CMS正在进行,但是由于老年代空间不足,需要尽快回收老年代里面的不再被使用的对象,这时停止所有的线程,同时终止CMS,直接进行Serial Old GC);
3.统计得到的Young GC晋升到老年代的平均大小大于老年代的剩余空间;

主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题。

然后,我们来逐一分析一下:
排除原因2:如果是原因2中两种情况,日志中会有特殊标识,目前没有。
排除原因3:根据GC日志,当时老年代使用量仅为20%,也不存在大于2G的大对象产生。
排除原因4:因为当时没有相关命令执行。
锁定原因1:根据日志发现Full GC后,Perm区变大了,推断是由于永久带空间不足容量扩展导致的。

找到原因后解决方法有两种:
1.通过把-XX:PermSize参数和-XX:MaxPermSize设置成一样,强制虚拟机在启动的时候就把永久带的容量固定下来,避免运行时自动扩容。
2.CMS默认情况下不会回收Perm区,通过参数CMSPermGenSweepingEnabled、CMSClassUnloadingEnabled ,可以让CMS在Perm区容量不足时对其回收。

猜你喜欢

转载自blog.csdn.net/sinat_30594513/article/details/88699217