【JVM系列】JVM启动参数与性能调优

JVM一直是面试中经常问到的知识点,除此之外,在项目性能调优上,也需要对其十分了解。本系列会从内存模型,再到整个对象的创建流程以及垃圾回收等,将JVM的知识以及面试常见题型全部讲解一遍。

【JVM系列】JVM内存模型详解

JVM一直是面试中经常问到的知识点,除此之外,在项目性能调优上,也需要对其十分了解。本系列会从内存模型,再到整个对象的创建流程以及垃圾回收等,将JVM的知识

【JVM系列】JVM对象创建与垃圾回收

JVM一直是面试中经常问到的知识点,除此之外,在项目性能调优上,也需要对其十分了解。本系列会从内存模型,再到整个对象的创建流程以及垃圾回收等,将JVM的知识

在前面的几篇文章中,说了一大堆JVM相关的东西,本篇文章主要介绍一下在哪设置JVM的启动参数、启动参数有哪些以及性能调优。

以Idea为例,平时我们可能只是点个运行就完事了,并不会想着去设置JVM的启动参数,所以很少关注这块。如下图所示,在Idea中,可在如下位置进行设置。

JVM的启动参数概念非常简单,分为三类:

  • 标准参数:
    • 例如:-version、-help,可以通过java -help命令来检索所有的标准参数。
  • X参数:
    • X表示非标准化参数,表示在将来的JVM版本中可能会发生变化,但是这类以-X开始的参数变化的比较小。可以通过java -X命令来检索所有的-X参数。
  • XX参数:
    • 非标准化参数,相对来说不稳定,随着JVM版本的变化可能会发生 变化,主要用于JVM调优和debug。
    • boolean类型:-XX:+ 或 -XX:- 某功能是否开启、关闭
    • KV设值类型:-XX:属性key=属性值value

下面,列举一些常见的参数。

最后,再说一下性能调优的实战。这个是图灵学院的一个直播课程上讲到的。

场景:有个系统,当流量上来了之后,就频繁发生Full GC,大概每过5-8分钟就发生一次。配置的启动参数如下图所示。

系统流程简化如下图所示。

要分析原因的话需要对整个系统比较熟悉,每个流程都要清楚。首先假定每个订单对象占用1KB,一台机器每秒生成300KB的订单对象。

整个下单环境,不单单只有订单的生成,还有其他的一些环节,例如库存、积分等等,那在此基础上,放大20倍,也就是说,每秒钟,一台服务器会产生300KB*20的对象。

为了保险,再放大10倍,避免有些环节漏了。所以,每秒每一台服务器会产生60MB的对象,而这些对象,在下单完成后,都变为垃圾对象,假设1秒后。

所以,上述分析,可由下图表示。

分析完整个流程产生对象的情况后,再次回到启动参数上。可以看到,初始堆内存设置成了3G,根据前面几篇文章的介绍,那对应新生代占1G,老年代占2G。而新生代中的伊甸区占800MB,两个幸存区各占100MB。

结合JVM内存模型,可以得出下图,会使这次的分析更加直观一点。计算得出,大概过14秒后,伊甸区被占满,会执行Minor GC。

而前面有介绍到,执行Minor GC时会STW(但是会很快)。STW时,正在进行的下单线程被挂起,但此时的对象是被引用的,所以并不会回收,前13秒的会被直接回收掉。等到STW结束,最后1秒产生的对象,按照之前的理解,会被挪到幸存区。

而问题的关键,就在于此。最后1秒产生的60MB对象,不会挪到性存区,而是直接到老年代。

前面几篇文章有讲到,如果是大对象或者存活年龄达到了阈值的对象,都会进入老年代,这只是其中的两种情况。下面再介绍两种情况,加起来共四种,是最常见的。

  • 空间分配担保:如果老年代 连续空间 大于 新生代对象总大小,就会进行MinorGC,否则,进而判断是否开启HandlerPromotionFailure,没有开启直接FullGC。如果开启了HanlerPromotionFailure, JVM会判断 老年代 的 最大连续内存空间 是否 大于 历次晋升的大小,如果小于直接执行FullGC,大于就MinorGC。

    • 假如大量对象在Minor GC后仍然存活,而Survivor空间是比较小的,这时就需要老年代进行分配担保,把Survivor无法容纳的对象放到老年代。
    • 老年代要进行空间分配担保,前提是老年代得有足够空间来容纳这些对象,但一共有多少对象在内存回收后存活下来是不可预知的,因此只好取之前每次垃圾回收后晋升到老年代的对象大小的平均值作为参考。使用这个平均值与老年代剩余空间进行比较,来决定是否进行Full GC来让老年代腾出更多空间。
  • 动态对象年龄判断:幸存区的对象年龄从小到大进行累加,当累加到年龄N时的综合大于50%,那把年龄N及以上的对象都放入老年代。

所以,在这个例子中,基于动态对象年龄判断,最后1秒产生的60MB对象,会直接进入老年代。那对于2G的老年代来说,假设极端的情况下,只存放这一种对象,那可以存放大概34批,每过14秒会进入一批,所以经过计算,刚好8分钟执行一次Full GC。

到这,原因已经分析出来了,接下来肯定是通过参数的设置,进行调整。其实,再扩展一下,可以理解为,如何通过参数调优,让其几乎不发生Full GC。

先来解决上面这个例子,直接给出调优参数,再分析。

其实很简单,通过-Xmn参数,把年轻代调大一点即可。当然,通过-XX:NewRatio参数,调整比例也行。调整完后,JVM模型如下所示,再分析一次,会发现问题就已经解决了。

到这,关于启动参数和性能调优就简单介绍到这了。由于作者目前也没有实战过,所以对于调优,暂时处于理论阶段,而关于启动参数,其实也还有很多没有讲到的,待后续补充。

人已赞赏
Java基础编程语言

【JVM系列】JVM对象创建与垃圾回收

2020-6-7 15:39:37

Java基础编程语言

【JVM系列】JVM常见面试题

2020-6-8 9:30:18

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
有新私信 私信列表
搜索