在调优之前,我们需要记住下面的原则:
1、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合)。
2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题。同时多数的Java应用不需要在服务器上进行GC优化。
3、减少使用全局变量和大对象,同时注意减少创建对象的数量;这也是Spring框架单例模式的一个优点。
4、在实际使用中,分析GC情况优化代码比优化GC参数要多得多,GC优化往往是最后的手段;
GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化。指标参考:
a.Minor GC执行时间不到50ms;
b.Minor GC执行不频繁,约10秒一次;
c.Full GC执行时间不到1s;
d.Full GC执行频率不算频繁,不低于10分钟1次;
1、性能定义:
①内存占用:垃圾收集器流畅运行所需要的内存数量。
②延迟:其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。
③吞吐量:指单位时间内系统处理用户的请求数。此处需要满足不考虑垃圾收集引起的停顿时间或内存消耗,能支撑应用达到的最高性能指标。
这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得,具体某一个属性或者两个属性的性能对应用来说比较重要,要基于应用的业务需求来确定。另外调优一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。
2、调优过程中的原则
a.每次minor GC都要尽可能多的收集垃圾对象,以减少应用程序发生Full GC的频率。将转移到老年代的对象数量降低到最小,减少Major GC/Full GC的执行时间。解决方法如下:
①调整新生代的大小到最合适;
②选择合适的GC收集器;
③设置老年代的大小为最合适;
④减少使用全局变量和大对象;
b.GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。
c.在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。
jvm调优是根据性能测试结果不断优化配置而多次迭代的过程。在达到每一个系统需求指标之前,之前的每个步骤都有可能经历多次迭代。
参考:https://www.cnblogs.com/xiaopaipai/p/10522794.html
通过在Tomcat的bin目录下的catalina.bat或catalina.sh文件中添加GC日志文件或者使用其他工具得到应用Full GC的日志信息。此处的Full GC必须是在满足应用需求的最高峰时期的GC日志,且满足如下测试需求:
1.测试时,启动参数采用JVM默认参数,不人为设置。
2.确保Full GC发生时,应用程序正处于稳定阶段。
主要需要了解的参数是:
1、年轻代使用内存大小。建议设置:1-1.5倍FullGC之后的老年代空间占用。
2、老年代使用内存大小。2-3倍FullGC后的老年代空间占用。
3、永久代使用内存大小。1.2-1.5倍FullGc后的永久带空间占用。
4、堆内存使用大小。3-4倍FullGC后的老年代空间占用。
PS:此处的内存调优是满足了内存的基本需求,但并不一定满足延迟和吞吐量的需求。
最佳实践是将花费在垃圾收集上的时间调整为执行时间的5%以内。JVM堆大小决定了JVM收集垃圾的频率和时间。可接受的垃圾收集速率是基于应用程序的需求,应在分析垃圾收集的实际时间和频率之间进行调整。如果设置较大的堆大小,则完全垃圾收集的速度较慢,但发生频率较低。如果根据内存需求设置堆大小,则完全垃圾收集会更快,但会更频繁地发生。但调整堆大小的目标是最大程度地减少JVM用于进行垃圾收集的时间,同时最大程度地增加程序执行业务逻辑的时间。为了确保基准测试期间的最佳性能,您可以设置较高的堆大小值,以确保在整个基准测试过程中不会进行垃圾回收。
在确定了应用程序的内存调优数据大小之后,我们需要再进行延迟性调优,因为对于此时堆内存大小,延迟性需求无法达到应用的需要,需要基于应用的情况来进行调试。这一步进行期间,我们可能会再次优化堆大小的配置,评估GC的持续时间和频率、以及是否需要切换到不同的垃圾收集器上。
在调优之前,我们需要知道系统的延迟需求是那些,以及对应的延迟可调优指标是那些。
a.应用程序可接受的平均停滞时间(重点关注): 此时间与测量的Minor GC持续时间进行比较。
b.可接受的Minor GC频率:Minor GC的频率与可容忍的值进行比较。
c.可接受的最大停顿时(重点关注):最大停顿时间与最差情况下Full GC的持续时间进行比较。
d.可接受的最大停顿发生的频率:基本就是Full GC的频率。
基于以上的要求,我们需要统计以下数据:
a.Minor GC的持续时间;
b.统计Minor GC的次数;
c.Full GC的最差持续时间;
d.最差情况下,Full GC的频率;
获取GC时间,通过jconsole工具可以获取程序运行时间,通过jstat -gcutil PID号 250 10可以获取GC时间并计算GC数据:
Minor GC平均耗时=YGCT/YGC(s)=0.021/3=0.007ms
Minor GC时间间隔=程序的运行时间/YGC=(30*60)/3=600S
Full GC平均耗时=FGCT/FGC(s)=0.03/1=0.03ms
Full GC时间间隔=程序的运行时间/FGC=(30*60)/1=1800S
1、年轻代大小调整说明:年轻代空间越大,Minor GC的GC时间越长,频率越低。如果想减少其持续时长,就需要减少其空间大小(按一定比例减少,否则将会过大增加GC频次,GC时间无优化效果)。如果想减小其频率,就需要加大其空间大小(按一定比例增加,否则将会过大增加GC时间,最大停顿时间明显)。
2、老年代大小调整说明:可以调整老年代的大小来调整Full GC的频率,如果FullGC持续时间过长,无法达到应用程序的最差延迟要求,就需要切换垃圾处理器,如切换为CMS。
3、最佳实践:
a.为了确保基准测试期间的最佳性能,您可以设置较高的堆大小值,以确保在整个基准测试过程中不会进行垃圾回收。
b.建议设置最小堆大小等于最大堆大小以最大程度地减少垃圾收集以及减少系统调整JVM资源的时间。
吞吐量调优主要是基于应用程序的吞吐量(例如每小时批处理系统能完成的任务数量,在吞吐量方面优化的系统,较长的 GC 停顿时间也是可以接受的)要求而来的,应用程序有一个综合的吞吐指标,这个指标基于整个应用的需求和测试而衍生出来的。当有应用程序的吞吐量达到或者超过预期的吞吐目标,整个调优过程就可以圆满结束了。如果出现调优后依然无法达到应用程序的吞吐目标,需要重新回顾吞吐要求,评估当前吞吐量和目标差距是否巨大,如果在20%左右,可以修改参数,加大内存,再次从头调试,如果巨大就需要从整个应用层面来考虑,设计以及目标是否一致了,重新评估吞吐目标。
对于JVM调优来说,提升吞吐量的性能调优的目标就是就是尽可能避免或者很少发生FullGC 或者Stop-The-World压缩式垃圾收集(CMS),因为这两种方式都会造成应用程序吞吐量降低。尽量在Minor GC阶段回收更多的对象,避免对象提升过快到老年代。
进行JVM调优时除了针对GC的次数和持续时间针对吞吐量、延迟进行的调优,还需要选择合适的垃圾回收器。
JVM参数调优参考:https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html