JVM · Java虚拟机原理 · JVM上语言·框架· 生态系统java 虚拟机(JVM)学习笔记

在金三银四的跳槽季中 你可能缺这一份JVM性能调优总结

2020-04-20  本文已影响0人  吃井不忘挖水人呢

JVM调优配置

-server JVM运行的模式之一, server模式才能进行逃逸分析, JVM运行的模式还有mix/client

-Xmx10m和-Xms10m:堆的大小

-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)

-XX:+PrintGC:打印GC日志

-XX:+EliminateAllocations:标量替换(默认打开)

-XX:-UseTLAB 关闭本地线程分配缓冲

-XX:+EliminateLocks可以开启同步消除,进行测试执行的效率

对栈上分配发生影响的参数就是三个,-server、-XX:+DoEscapeAnalysis和-XX:+EliminateAllocations,任何一个关闭都不会发生栈上分配,因为启用逃逸分析和标量替换默认是打开的,所以,在我们的例子中,JVM的参数只用-server一样可以有栈上替换的效果

2、垃圾回收器的选择

2、1内存分配与回收策略

对象优先在Eden分配,如果说Eden内存空间不足,就会发生Minor GC

大对象直接进入老年代,大对象:需要大量连续内存空间的Java对象,比如很长的字符串和大型数组,1、导致内存有空间,还是需要提前进行垃圾回收获取连续空间来放他们,2、会进行大量的内存复制。

-XX:PretenureSizeThreshold 参数 ,大于这个数量直接在老年代分配,缺省为0 ,表示绝不会直接分配在老年代。

长期存活的对象将进入老年代,默认15岁,-XX:MaxTenuringThreshold调整

动态对象年龄判定,为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄

空间分配担保:新生代中有大量的对象存活,survivor空间不够,当出现大量对象在MinorGC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代.只要老年代的连续空间大于新生代对象的总大小或者历次晋升的平均大小,就进行Minor GC,否则FullGC。

2、2垃圾搜集算法简介

标记-清除算法(Mark-Sweep)

复制算法(Copying)

复制算法

标记-整理算法(Mark-Compact)

标记-整理算法

2、3 JVM默认垃圾回收器

查看命令:java -XX:+PrintCommandLineFlags -version

垃圾收集相关的常用参数

也就是说,打开此开关,使用的垃圾收集器是:新生代(Parallel Scavenge),老年代(Ps MarkSweep)组合。

jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.9 默认垃圾收集器G1

2、4垃圾回收期列表

2、5 垃圾回收器搭配建议

如果是线上系统,比价看重服务的响应速度,则建议使用:ParNew+CMS + Serial Old(替补)

如果是跑批系统,比较看重系统的吞吐量,则建议使用:Parallel Scavenge+Parallel Old

3、与JIT(Just-In-Time Compiler)编译器相关的优化(JVM调优)

JIT编译器:从.class文件编译成不同机器系统的机器码的时候所使用到的编译器的叫做JIT编译器(即时编译器)。

热点编译:对于程序来说,通常只有一部分代码被经常执行,这些关键代码被称为应用的热点,执行的越多就认为是越热。将这些代码编译为本地机器特定的二进制码,可以有效提高应用性能。

编译器类型:

-server,更晚编译,但是编译后的优化更多,性能更高

-client,很早就开始编译

-XX:+TieredCompilation,开启分层编译,可以让jvm在启动时启用client编译,随着代码变热后再转为server编译。

缺省编译器取决于机器位数、操作系统和CPU数目。32位的机器上,一般默认都是client编译,64位机器上一般都是server编译,多核机器一般是server编译。

mix mode 一般指编译时机:

-Xint表示禁用JIT,所有字节码都被解释执行,这个模式的速度最慢的。

-Xcomp表示所有字节码都首先被编译成本地代码,然后再执行。

-Xmixed,默认模式,让JIT根据程序运行的情况,有选择地将某些代码编译成本地代码。

-Xcomp和-Xmixed到底谁的速度快,针对不同的程序可能有不同的结果,基本还是推荐用默认模式。

4、GC调优(JVM调优)

调优的原则和步骤

1、 大多数的java应用不需要GC调优

2、 大部分需要GC调优的的,不是参数问题,是代码问题

3、 在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

4、 GC调优是最后的手段

GC调优的最重要的三个选项:

第一位:选择合适的GC回收器

第二位:选择合适的堆大小

第三位:选择年轻代在堆中的比重

调优步骤:

1,监控GC的状态

使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化;

2,分析结果,判断是否需要优化

如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;

注:如果满足下面的指标,则一般不需要进行GC:

Minor GC执行时间不到50ms;

Minor GC执行不频繁,约10秒一次;

Full GC执行时间不到1s;

Full GC执行频率不算频繁,不低于10分钟1次;

3,调整GC类型和内存分配

如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择;

4,不断的分析和调整

通过不断的试验和试错,分析并找到最合适的参数

5,全面应用参数

如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。

推荐策略

年轻代大小选择

响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发生的频率也是最小的.同时,减少到达年老代的对象.

吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用.

避免设置过小.当新生代设置过小时会导致:1.YGC次数更加频繁 2.可能导致YGC对象直接进入年老代,如果此时年老代满了,会触发FGC.

年老代大小选择

响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数.如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间.最优化的方案,一般需要参考以下数据获得:

并发垃圾收集信息、持久代并发收集次数、传统GC信息、花在年轻代和年老代回收上的时间比例。

吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代.原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象

5、JDK为我们提供的工具

JVM dump分析工具MAT

关于工具使用,参数含义,请参考https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483966&idx=1&sn=dfa3375d36aa2c0c25a775522e381e62&chksm=ebf6da41dc815357e0d53c73865a23f41219e75bac5a4d510bfa31cc51594b59a20e2e4f6cb8&scene=21#wechat_redirect

6、JVM老年代和新生代的比例问题

Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。

在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。

堆的内存模型大致为:

从图中可以看出: 堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。

(本人使用的是 JDK1.6,以下涉及的 JVM 默认值均以该版本为准。)

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。

默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

7、空间分配担保

在发生minor gc之前,虚拟机会检测 : 老年代最大可用的连续空间>新生代all对象总空间

1、满足,minor gc是安全的,可以进行minor gc。

2、不满足,虚拟机查看HandlePromotionFailure参数:

(1)为true,允许担保失败,会继续检测老年代最大可用的连续空间>历次晋升到老年代对象的平均大小。若大于,将尝试进行一次minor gc,若失败,则重新进行一次full gc。

(2)为false,则不允许冒险,要进行full gc(对老年代进行gc)。

配置:-XX:+HandlePromotionFailure

8、总结性发言

调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的。也没有一个放之四海而皆准的配置或者公式。

写完后发现此文,作为补充:https://zhuanlan.zhihu.com/p/34426768

需要MySQL资料大礼包高清完整版的老铁请转发+关注,然后私信回复“MySQL”获得免费领取方式

上一篇下一篇

猜你喜欢

热点阅读