JVM调优

参考文档;

java3y《对线面试官》

 为什么要进行调优

避免OOM

减少FullGC频率(导致stw)

充分利用系统性能,合理使用硬件资源

优化原则

尽可能让对象都在新生代里面分配和回收,尽量别让太多对象向频繁进入老年代,避免频繁对老年代进行垃圾回收

同时给系统充足的内存大小,避免新生代频繁进行垃圾回收

 

什么情况会有FULL GC

https://zhuanlan.zhihu.com/p/574921464

    1. System.gc()方法的调用
      此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。
    2. 老年代空间不足
      在Survivor区域的对象满足晋升到老年代的条件时,晋升进入老年代的对象大小大于老年代的可用内存,这个时候会触发Full GC。,当执行Full GC后空间仍然不足,则抛出错误:java.lang.OutOfMemoryError: Java heap space 。为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组
    3. Metaspace区内存达到阈值
      从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存,而不是堆内存,也就是说在默认情况下Metaspace的大小只与本地内存大小有关。-XX:MetaspaceSize=21810376B(约为20.8MB)超过这个值就会引发Full GC,这个值不是固定的,是会随着JVM的运行进行动态调整的,与此相关的参数还有多个,详细情况请参考这篇文章jdk8 Metaspace 调优
    4. 统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间 Survivor区域对象晋升到老年代有两种情况:
      (1)一种是给每个对象定义一个对象计数器,如果对象在Eden区域出生,并且经过了第一次GC,那么就将他的年龄设置为1,在Survivor区域的对象每熬过一次GC,年龄计数器加一,等到到达默认值15时,就会被移动到老年代中,默认值可以通过-XX:MaxTenuringThreshold来设置。
      (2)另外一种情况是如果JVM发现Survivor区域中的相同年龄的对象占到所有对象的一半以上时,就会将大于这个年龄的对象移动到老年代,在这批对象在统计后发现可以晋升到老年代,但是发现老年代没有足够的空间来放置这些对象,这就会引起Full GC。
    5. 堆中产生大对象超过阈值
      这个参数可以通过-XX:PretenureSizeThreshold进行设定,大对象或者长期存活的对象进入老年代,典型的大对象就是很长的字符串或者数组,它们在被创建后会直接进入老年代,虽然可能新生代中的Eden区域可以放置这个对象,在要放置的时候JVM如果发现老年代的空间不足时,会触发GC。
    6. 老年代连续空间不足
      JVM如果判断老年代没有做足够的连续空间来放置大对象,那么就会引起Full GC,例如老年代可用空间大小为200K,但不是连续的,连续内存只要100K,而晋升到老年代的对象大小为120K,由于120>100的连续空间,所以就会触发Full GC。
    7. 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:设置每个线程的堆栈大小,减小这个值意味着同样的内存空间可以生成更多的线程。

 

posted on 2023-03-12 15:55  or追梦者  阅读(11)  评论(0编辑  收藏  举报