JVM调优
参考文档;
java3y《对线面试官》
为什么要进行调优
避免OOM
减少FullGC频率(导致stw)
充分利用系统性能,合理使用硬件资源
优化原则
尽可能让对象都在新生代里面分配和回收,尽量别让太多对象向频繁进入老年代,避免频繁对老年代进行垃圾回收
同时给系统充足的内存大小,避免新生代频繁进行垃圾回收
什么情况会有FULL GC
https://zhuanlan.zhihu.com/p/574921464
- System.gc()方法的调用
此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。 - 老年代空间不足
在Survivor区域的对象满足晋升到老年代的条件时,晋升进入老年代的对象大小大于老年代的可用内存,这个时候会触发Full GC。,当执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError: Java heap space 。为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。 - Metaspace区内存达到阈值
从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。-XX:MetaspaceSize=21810376B(约为20.8MB)超过这个值就会引发Full GC,这个值不是固定的,是会随着JVM的运行进行动态调整的,与此相关的参数还有多个,详细情况请参考这篇文章jdk8 Metaspace 调优 - 统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间 Survivor区域对象晋升到老年代有两种情况:
(1)一种是给每个对象定义一个对象计数器,如果对象在Eden区域出生,并且经过了第一次GC,那么就将他的年龄设置为1,在Survivor区域的对象每熬过一次GC,年龄计数器加一,等到到达默认值15时,就会被移动到老年代中,默认值可以通过-XX:MaxTenuringThreshold来设置。
(2)另外一种情况是如果JVM发现Survivor区域中的相同年龄的对象占到所有对象的一半以上时,就会将大于这个年龄的对象移动到老年代,在这批对象在统计后发现可以晋升到老年代,但是发现老年代没有足够的空间来放置这些对象,这就会引起Full GC。 - 堆中产生大对象超过阈值
这个参数可以通过-XX:PretenureSizeThreshold进行设定,大对象或者长期存活的对象进入老年代,典型的大对象就是很长的字符串或者数组,它们在被创建后会直接进入老年代,虽然可能新生代中的Eden区域可以放置这个对象,在要放置的时候JVM如果发现老年代的空间不足时,会触发GC。 - 老年代连续空间不足
JVM如果判断老年代没有做足够的连续空间来放置大对象,那么就会引起Full GC,例如老年代可用空间大小为200K,但不是连续的,连续内存只要100K,而晋升到老年代的对象大小为120K,由于120>100的连续空间,所以就会触发Full GC。 - CMS GC时出现promotion failed和concurrent mode failure
提升失败(promotion failed),在 Minor GC 过程中,Survivor Unused 可能不足以容纳 Eden 和另一个 Survivor 中的存活对象, 那么多余的将被移到老年代, 称为过早提升(Premature Promotion)。这会导致老年代中短期存活对象的增长, 可能会引发严重的性能问题。 再进一步, 如果老年代满了, Minor GC 后会进行 Full GC, 这将导致遍历整个堆, 称为提升失败(Promotion Failure)。
在 CMS 启动过程中,新生代提升速度过快,老年代收集速度赶不上新生代提升速度。在 CMS 启动过程中,老年代碎片化严重,无法容纳新生代提升上来的大对象,这是因为CMS采用标记清理,会产生连续空间不足的情况,这也是CMS的缺点。
条件
知识
工具
数据
经验
思路
通过性能监控工具监控然后再做判断
发现Major GC的次数比较多,可以增加老年代的大小,同时也要观察在进行Major GC后是否会清理掉较多的空间,如果是的话说明老年代有很多并不需长期存在的对象,可以考虑增大-XX:MaxTenuringThreshold的值,避免对象太早进入老年代
一般来说的优化顺序
通常是关系型数据库先到瓶颈,首先排查数据库问题
索引、语句、是否引入分布式缓存、是否需要分库分表等
然后会考虑扩容
系统压力过大或者硬件能力不足
接着是应用代码层面上的排查优化
然后jvm层面上优化
最后是网络和操作系统层面排查
jvm调优可以参考几个指标;吞吐量、停顿时间、垃圾回收频率
基于上述指标,可能需要调整的地方:
内存区域大小及相关策略(比如堆内存各占比、-Xmx设置堆最大值、-Xms设置堆初始值、-Xmn年轻代大小、-XX:SurvivorRatio伊甸园区和幸存区的比例等)
垃圾回收器(选择合适的垃圾回收器,垃圾回收器的参数调优)
相关参数
-
-Xms3550m:初始化堆大小,一开始使用的内存大小,如果超出该大小就会自动扩容;
-
-Xmx3550m:最大堆大小,初始化系统就会分配,虽然一开始使用的是初始堆大小,但是可以根据情况动态扩容,但是不能超过最大堆大小,否则会抛出outOfMemoryError;
-
-Xmn:设置年轻代的大小;
-
-XX:SurvivorRatio=4:年轻代中Eden区与一个Survivor区的比值。例如是设置为4则代表两个Survivor和Eden的比值为2:4,一个Survivor占用的整个年轻代的1/6;
-
-XX:NewRatio=3:新生代:老年代1:3;
-
-XX:MetaspaceSize=256m 元空间初始值(这个是JDK8以后的参数,因为JDK8移除了永久代取而代之的是元空间,如果是JDK7或者是以前的版本,可以使用-XX:PermSize=256m以及XX:MaxPermSize=512m);
-
-XX:MaxMetaspaceSize=512m 元空间最大值(注意这里是堆外内存,他是不计入堆大小内存中的);
-
-Xss=1m:设置每个线程的堆栈大小,减小这个值意味着同样的内存空间可以生成更多的线程。
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.