1、掌握 GC 相关的 VM 参数,会基本的空间调整
2、掌握相关的工具
3、明白一点:调优和应用、环境有关,没有放之四海而皆准的法则。
内存、锁竞争、cpu 占用、io
需要根据你的实际业务场景选择合适的回收器,如果是互联网企业,可能更关注的是低延迟,即响应时间更短,可以选择 CMS、G1、ZGC 等。
如果是进行科学运算,可能追求的是高吞吐量,延长一点点响应时间也是没有问题的,只能选择 ParallelGC。
查看 FullGC 前后的内存占用,如果你的虚拟机经常发生 GC,需要考虑下面几个问题
1、数据是不是太多了?是不是加载了不必要的数据到内存里面。
2、数据表现是否太臃肿?能用基本类型就不用包装类型。
3、是否存在内存泄漏?一直向集合里面添加元素但不移除。
1、所有的 new 操作的内存分配非常廉价
2、死亡对象的回收代价是零
3、大部分对象用过即死
4、Minor GC 的时间远远低于 Full GC
使用 -Xmn 参数来配置新生代内存的大小。
如果新生代内存设置的小了,肯定是不太好,因为新生代小,可用空间就少,创建对象时一旦发现内存不足。就会触发 minorGC。
如果新生代内存设置的很大,肯定也是不好,因为新生代内存大了,那么相对而言老年代的内存肯定就小了,触发老年代的 fullGC 会更频繁,fullGC 比 minorGC STW 的时间更长。
Oracle 给了一个建议,即新生代内存大于堆内存 25% ,小于堆内存 50% 的比例,即堆内存的 四分之一 到 二分之一 之间。还是稍微大点好。
新生代能容纳所有【 并发量*(请求-响应)】的数据。
幸存区大到能够保留【当前活跃对象+需要晋升的对象】。
晋升阈值配置得当,让长时间存活对象尽快晋升。
- # 调整最大晋升阈值
- -XX:MaxTenuringThreshold=threshold
-
- # 打印晋升的一些详细信息
- -XX:+PrintTenuringDistribution
1、CMS 的老年代内存越大越好。
2、先尝试不做调优,如果没有 FullGC ,就证明系统已经很不错了,否则先尝试调优新生代。
3、如果老年代经常发生 FullGC,先观察 FullGC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3 。
- # 当老年代内存达到 percent 时就进行垃圾回收
- -XX:CMSInitiatingOccupancyFraction=percent
现象:程序运行期间 FullGC 和 MinorGC 频繁。
分析:GC 特别频繁说明空间比较紧张。如果是新生代的空间紧张,等到业务高峰期来了,大量的对象被创建,很快就把我的新生代的空间塞满了,塞满了之后还会造成一个后果,幸存区由于空间紧张,它里面对象的晋升阈值就会降低,导致很多生存周期很短的对象也会被晋升到老年代去。此时老年代里面就存储了很多的这种生存周期很短的对象,进一步触发老年代的这种 FullGC 的频繁发生。
解决:通过工具观察堆空间的大小,确实发现新生代的内存设置的太小了,根据之前的经验,内存优化需要先从新生代开始,即增大新生代的内存。
现象:请求高峰期发生 Full GC,单次暂停时间特别长(CMS)。
分析:首先查看 GC 日志,看下 CMS 哪个阶段耗费的时间较长,假设是重新标记阶段耗时较长。
解决:可以使用 -XX:+CMSScavengeBeforeRemark 参数,使得在重新标记之前先对新生代的对象做一次垃圾清理,清理完对象少了,重新标记阶段的耗时也就短了。
现象:老年代充裕情况下,发生 Full GC (1.7)。
分析:CMS 可能由于空间不足导致并发失败,或者是空间碎片较多都会导致 FullGC,但是经过排查,在 GC 日志里面并没有发现并发失败的错误提示,说明老年代的空间是充裕的。此时想到部署的 jdk 版本为 1.7,不是现在的 1.8。1.8 是元空间作为方法区的实现,而 1.7 以前的 jdk 采用的是永久代作为方法区的实现。在 jdk1.7 及以前的版本中,永久代的空间不足也会导致 FullGC
解决:增加永久代的内存即可。