🏆「作者推荐」【JVM 性能调优】JVM 分析与调优技巧分析(原理篇)
JVM 空间说明
在 JDK1.7 及以前,HotSpot 虚拟机将 java 类信息、常量池、静态变量、即时编译器编译后的代码等数据,存储在 Perm(永久带)里(对于其他虚拟机如 BEA JRockit、IBM J9 等是不存在永久带概念的),类的元数据和静态变量在类加载的时候被分配到 Perm 里,当常量池回收或者类被卸载的时候,垃圾收集器会回收这一部分内存,但效果不太理想。
JDK1.8 时,HotSpot 虚拟机对 JVM 模型进行了改造,将类元数据放到了本地内存中,将常量池和静态变量放到了 Java 堆里,HotSpot VM 将会为类的元数据明确的分配与释放本地内存,在这种架构下,类元数据就突破了-XX:MaxPermSize 的限制,所以此配置已经失效,现在可以使用更多的本地内存。这样一定程度上解决了原来在运行时生成大量的类,从而经常 Full GC 的问题——如运行时使用反射、代理等。
干货要点
可以发现最明显的一个变化是元空间从虚拟机转移到了本地内存。默认情况下,元数据空间大小仅受限于本地内存,这意味着以后不会因为永久代大小不够而抛出 OOM 异常了。
jdk1.8 以前,HotSpot VM 将 class 和类的 jar 包数据存储在 PermGen 里,PermGen 大小是固定的,而且项目之间无法公用公有的 class,所以很容易碰到 OOM 异常。改成 MetaSpace 后。
各个项目会共享同样的 class 空间。比如多个项目都引用了 apache-common 包,在 MetaSpace 中只会存储一份的 apache-common 的 class,提高了内存的利用率,垃圾回收更有效。
Minor GC—复制算法具体过程:
将 Eden 和 S0 中还存活着的对象一次性的复制到 S1 中,并且清理掉 Eden 与 S0 的空间。如果 S1 放不下还存活着的对象,那这些对象将通过分配担保机制进入老年代。【原理上随时保持 S0 和 S1 有一个是空的,用来存下一次的对象】
Eden 区快满的时候,会进行上一步类似操作,将 Eden 和 S1 区的年纪大的对象放到 S0 区【此时 S1 区就是空的】
直到 Eden 区快满,S0 或者 S1 也快满的时候,这时候就把这两个区的年纪大的对象放到 Old 区。
依次循环,直到 Old 区也快满的时候,Eden 区也快满的时候,会对整个这一块内存区域进行一次大清洗(FullGC),腾出内存,为之后的对象创建,程序运行腾地方。
新生代 GC(Minor GC):指发生在新生代的垃圾回收动作,因为 java 对象大多具备朝生夕灭的特征,所以 Minor GC 发生的特别频繁,一般回收速度也很快。
老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC,至少会伴随一次的 MinorGC(但非绝对,在 Parallel Scavenge 收集器的收集策略里就有直接进行 Minor GC 的策略选择过程)。
Major GC 的速度一般比 Minor GC 慢 10 倍以上。
升级 JDK1.8 之后,上面的 perm 配置已经变成
MetaspaceSize 如果不做配置,通过 jinfo 查看默认 MetaspaceSize 大小(约 21M),MaxMetaspaceSize 很大很大,前面说过 MetaSpace 只受本地内存大小限制。
干货: MetaspaceSize 为触发 FullGC 的阈值,默认约为 21M,如做了配置,最小阈值为自定义配置大小。空间使用达到阈值,触发 FullGC,同时对该值扩大。当然如果元空间实际使用小于阈值,在 GC 的时候也会对该值缩小。MaxMetaspaceSize 为元空间的最大值,如果设置太小,可能会导致频繁 FullGC,甚至 OOM。
-XX 参数被称为不稳定参数,之所以这么叫是因为此类参数的设置很容易引起 JVM 性能上的差异,使 JVM 存在极大的不稳定性。如果此类参数设置合理将大大提高 JVM 的性能及稳定性。
不稳定参数语法规则:
布尔类型参数值
-XX:+ '+'表示启用该选项
-XX:- '-'表示关闭该选项
数字类型参数值:
-XX:= 给选项设置一个数字类型值,可跟随单位,例如:'m’或’M’表示兆字节;'k’或’K’千字节;'g’或’G’千兆字节。32K 与 32768 是相同大小的。
字符串类型参数值:
-XX:= 给选项设置一个字符串类型值,通常用于指定一个文件、路径或一系列命令列表。
JVM 参数示例
解析:
-Xmx4g:堆内存最大值为 4GB。
-Xms4g:初始化堆内存大小为 4GB 。
-Xmn1200m:设置年轻代大小为 1200MB。增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8。
-Xss512k:设置每个线程的堆栈大小。JDK5.0 以后每个线程堆栈大小为 1MB,以前每个线程堆栈大小为 256K。应根据应用线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右。
-XX:NewRatio=4:设置年轻代(包括 Eden 和两个 Survivor 区)与年老代的比值(除去持久代)。设置为 4,则年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5
-XX:SurvivorRatio=8:设置年轻代中 Eden 区与 Survivor 区的大小比值。设置为 8,则两个 Survivor 区与一个 Eden 区的比值为 2:8,一个 Survivor 区占整个年轻代的 1/10
-XX:PermSize=100m:初始化永久代大小为 100MB。
-XX:MaxPermSize=256m:设置持久代大小为 256MB。
-XX:MaxTenuringThreshold=15:设置垃圾最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
JVM 调优目标
何时需要做 jvm 调优?
heap 内存(老年代)持续上涨达到设置的最大内存值;Full GC 次数频繁;GC 停顿时间过长(超过 1 秒);应用出现 OutOfMemory 等内存异常;应用中有使用本地缓存且占用大量内存空间;系统吞吐量与响应性能不高或下降。JVM 调优原则
多数的 Java 应用不需要在服务器上进行 JVM 优化;
多数导致 GC 问题的 Java 应用,都不是因为我们参数设置错误,而是代码问题;
在应用上线之前,先考虑将机器的 JVM 参数设置到最优(最适合);
减少创建对象的数量;
减少使用全局变量和大对象;
JVM 优化是到最后不得已才采用的手段;
在实际使用中,分析 GC 情况优化代码比优化 JVM 参数更好;
JVM 调优目标
GC 低停顿;
GC 低频率;
低内存占用;
高吞吐量;
JVM 调优量化目标(示例):
Heap 内存使用率 <= 70%;
Old generation 内存使用率<= 70%;
avgpause <= 1 秒;
Full gc 次数 0 或 avg pause interval >= 24 小时 ;
注意:不同应用,其 JVM 调优量化目标是不一样的。
JVM 调优经验
JVM 调优经验总结
JVM 调优的一般步骤为:
第 1 步:分析 GC 日志及 dump 文件,判断是否需要优化,确定瓶颈问题点;
第 2 步:确定 JVM 调优量化目标;
第 3 步:确定 JVM 调优参数(根据历史 JVM 参数来调整);
第 4 步:调优一台服务器,对比观察调优前后的差异;
第 5 步:不断的分析和调整,直到找到合适的 JVM 参数配置;
第 6 步:找到最合适的参数,将这些参数应用到所有服务器,并进行后续跟踪。
JVM 调优重要参数解析
注意:不同应用,其 JVM 最佳稳定参数配置是不一样的。
配置:
重要参数(可调优)解析:
触发 Full GC 的场景及应对策略
年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代 GC 称为 MajorGC,而 Full GC 是对整个堆来说的,在最近几个版本的 JDK 里默认包括了对永生带即方法区的回收(JDK8 中无永生带了),出现 Full GC 的时候经常伴随至少一次的 Minor GC,但非绝对的。MajorGC 的速度一般会比 Minor GC 慢 10 倍以上。
触发 Full GC 的场景及应对策略:
System.gc()方法的调用,应对策略:通过-XX:+DisableExplicitGC 来禁止调用 System.gc ;
老年代代空间不足,应对策略:让对象在 Minor GC 阶段被回收,让对象在新生代多存活一段时间,不要创建过大的对象及数组;
永生区空间不足,应对策略:增大 PermGen 空间
GC 时出现 promotionfailed 和 concurrent mode failure,应对策略:增大 survivor space
Minor GC 后晋升到旧生代的对象大小大于老年代的剩余空间,应对策略:增大 Tenured space 或下调 CMSInitiatingOccupancyFraction=60
内存持续增涨达到上限导致 Full GC ,应对策略:通过 dumpheap 分析是否存在内存泄漏
Gc 日志分析工具
借助 GCViewer 日志分析工具,可以非常直观地分析出待调优点。
可从以下几方面来分析:
Memory,分析 Totalheap、Tenuredheap、Youngheap 内存占用率及其他指标,理论上内存占用率越小越好;
Pause ,分析 Gc pause、Fullgc pause、Total pause 三个大项中各指标,理论上 GC 次数越少越好,GC 时长越小越好;
版权声明: 本文为 InfoQ 作者【李浩宇/Alex】的原创文章。
原文链接:【http://xie.infoq.cn/article/a05e8c191dcf06ee6d4b67117】。文章转载请联系作者。
评论