JVM 1.8 调优小结
JVM GC 模型
.JVM 调优参考
. JVM 收集器选择
一篇比较好的参考:https://lijie.blog.csdn.net/article/details/105299403
JVM GC 模型
GC:
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。
新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的类都是在伊甸区被new出来的。
幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。
当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。
若幸存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再移动到养老区。
若养老区也满了,那么这个时候将产生Major GC(FullGC),进行养老区的内存清理。
若养老区执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。 如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二: (1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。 (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。 ----内存溢出;内存泄漏
.JVM 调优参考
推荐阅读: 参考: https://www.cnblogs.com/jpfss/p/9753215.html
总结: 百万连接,百亿吞吐量服务的JVM性能调优实战 >>https://my.oschina.net/LucasZhu/blog/2056232
最后,对于长连接,push一类的海量服务端应用,16G内存8核心,推荐的JVM参数如下 jdk 1.7 14g->13g
-Xms13g -Xmx13g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4
-XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=70 -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled
-XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
JDK1.8
-Xms13g -Xmx13g -Xss512k -XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:NewSize=11g -XX:MaxNewSize=11g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC
-XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC
-XX:+CMSScavengeBeforeRemark -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0
-XX:-ReduceInitialCardMarks -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime
-XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
这样可以保证大多数对象在new区域就销毁,并且到了old区,remark之前先yong gc,然后再来一次cms old gc,将old gc控制在毫秒级别
执行启动设置Jvm参数的操作。
java -jar -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC myApp-1.0.0.jar
关于这些设置的JVM参数是什么意思,请参考第二步中的oracle官方给出的调优文档。
我在这边简单说一下: .JVM 调优参考
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆默认大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2) 配置新生代中 eden from to 比例关系 8 1 1
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)
-
-XX:SurvivorRatio=2这个参数配置什么意思
-
* SurvivorRatio=2 配置新生代中 eden from to 比例关系 2 1 1
知识点:
JDK8之后把-XX:PermSize 和 -XX:MaxPermGen移除了,取而代之的是
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
JDK 8开始把类的元数据放到本地化的堆内存(native heap)中,这一块区域就叫Metaspace,中文名叫元空间。
使用本地化的内存有什么好处呢?最直接的表现就是java.lang.OutOfMemoryError: PermGen 空间问题将不复存在,因为默认的类的元数据分配只受本地内存大小的限制,也就是说本地内存剩余多少,理论上Metaspace就可以有多大(貌似容量还与操作系统的虚拟内存有关?这里不太清楚),这解决了空间不足的问题。不过,让Metaspace变得无限大显然是不现实的,因此我们也要限制Metaspace的大小:使用-XX:MaxMetaspaceSize参数来指定Metaspace区域的大小。JVM默认在运行时根据需要动态地设置MaxMetaspaceSize的大小。
GC模式
G1中提供了三种模式垃圾回收模式,young gc、mixed gc 和 full gc,在不同的条件下被触发。
young gc
发生在年轻代的GC算法,一般对象(除了巨型对象)都是在eden region中分配内存,当所有eden region被耗尽无法申请内存时,就会触发一次young gc,这种触发机制和之前的young gc差不多,
执行完一次young gc,活跃对象会被拷贝到survivor region或者晋升到old region中,空闲的region会被放入空闲列表中,等待下次被使用。
参数 含义
-XX:MaxGCPauseMillis 设置G1收集过程目标时间,默认值200ms
-XX:G1NewSizePercent 新生代最小值,默认值5%
-XX:G1MaxNewSizePercent 新生代最大值,默认值60%
mixed gc
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一个混合的垃圾收集器,即mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region,这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制。
那么mixed gc什么时候被触发?
先回顾一下cms的触发机制,如果添加了以下参数:
-XX:CMSInitiatingOccupancyFraction=80
-XX:+UseCMSInitiatingOccupancyOnly
当老年代的使用率达到80%时,就会触发一次cms gc。相对的,mixed gc中也有一个阈值参数 -XX:InitiatingHeapOccupancyPercent,当老年代大小占整个堆大小百分比达到该阈值时,会触发一次mixed gc.
mixed gc的执行过程有点类似cms,主要分为以下几个步骤:
initial mark: 初始标记过程,整个过程STW,标记了从GC Root可达的对象
concurrent marking: 并发标记过程,整个过程gc collector线程与应用线程可以并行执行,标记出GC Root可达对象衍生出去的存活对象,并收集各个Region的存活对象信息
remark: 最终标记过程,整个过程STW,标记出那些在并发标记过程中遗漏的,或者内部引用发生变化的对象
clean up: 垃圾清除过程,如果发现一个Region中没有存活对象,则把该Region加入到空闲列表中
full gc
如果对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发一次full gc,G1的full gc算法就是单线程执行的serial old gc,会导致异常长时间的暂停时间,需要进行不断的调优,尽可能的避免full gc.
链接:https://www.jianshu.com/p/0f1f5adffdc1
. JVM 收集器选择
垃圾回收器的选择 -> 原文链接:https://blog.csdn.net/zhaodongchao1992/article/details/106566984/
除非您的应用程序有非常严格的暂停时间要求,否则请先运行您的应用程序并允许VM选择收集器。如有必要,请调整堆大小以提高性能。如果性能仍然不能达到您的目标,请使用以下准则作为选择收集器的起点。
1 如果应用程序的数据集较小(最大约100 MB),则选择带有选项-XX:+ UseSerialGC的串行收集器。
2 如果应用程序将在单个处理器上运行,并且没有暂停时间要求,则让VM选择收集器,或通过选项-XX:+ UseSerialGC选择串行收集器。
3 如果(a)峰值应用程序性能是第一要务,并且(b)没有暂停时间要求或可接受1秒或更长时间的暂停,则让VM选择收集器,或使用-XX:+ UseParallelGC选择并行收集器。
4 如果响应时间比整体吞吐量更重要,并且垃圾收集暂停时间必须保持小于1秒,那么请使用-XX:+UseConcMarkSweepGC或-XX:+ UseG1GC选择并发收集器。
这些准则仅为选择收集器提供了一个起点,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度。暂停时间对这些因素特别敏感,因此前面提到的1秒阈值仅是近似值:在许多数据大小和硬件组合上,并行收集器的暂停时间将超过1秒。相反,在某些组合上,并发收集器可能无法将暂停时间保持在1秒以内
我比较看着响应时间,因此这里选择使用标记清除算法的CMS垃圾回收器,使用配置:-XX:+UseConcMarkSweepGC
-XX:+PrintFlagsFinal 可以打印jvm的详细配置信息
-XX:+PrintTenuringDistribution 可以打印新生代到老年代的阀值和对象的寿命。
5 元数据空间大小的分配
在Java8中,移除了之前的永久代,将类的元数据信息存放在一块非堆的本地内存中,这一块区域被称之为元数据区(Metaspace),不设置的话元数据空间的大小有机器内存的大小来决定。在实际情况最好是设置一个合适的值。
做如下配置:
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
6 直接内存大小的分配
直接内存顾名思义就是机器上的一块内存。也是一块非堆的内存。直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
这篇文章对直接内存有详细说明:https://blog.csdn.net/y3over/article/details/88791958
jvm设置参数如下(直接内存默认大小为64m):
-XX:MaxDirectMemorySize=1024m
-XX:+DisableExplicitGC
-XX:+DisableExplicitGC 禁止在代码中显示调用System.gc() 。因为在使用直接内存时会使用DirectByteBuffer对象。它在分配直接内存时会经常调用System.gc(),为了减少gc此时。线上环境建议禁止显示调用System.gc()
综上得到如下配置:
-XX:MaxDirectMemorySize=1024m //一块非堆的内存
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆默认大小)
-Xmn256m (新生代大小)
-Xss256k (设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。)
-XX:SurvivorRatio=8 (新生代分区比例 8:2) 配置新生代中 eden from to 比例关系 8 1 1
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器) 或者: -XX:+UseG1GC
-XX:+PrintGCDetails (打印详细的GC日志)
-Xloggc:/var/app/loggs/gc.log
-XX:+UseConcMarkSweepGC
-XX:+PrintTenuringDistribution
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/app/log/dump/java_heapdump.hprof
-XX:+DisableExplicitGC //禁止在代码中显示调用System.gc()
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。
CMS:
CMS 收集器:老年代。是一种以获取最短回收停顿时间为目标的收集器,适用于互联网站或者 B/S 系统的服务端上。
特点:
针对老年代,采用标记-清楚法清除垃圾;
基于"标记-清除"算法(不进行压缩操作,产生内存碎片);
以获取最短回收停顿时间为目标;
并发收集、低停顿;
CMS收集器有3个明显的缺点:1.对CPU资源非常敏感、2.无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败、3.产生大量内存碎片
垃圾收集线程与用户线程(基本上)可以同时工作
使用方式: 设置垃圾收集器:"-XX:+UseConcMarkSweepGC":指定使用CMS收集器;
G1
G1 收集器:分代收集器。当今收集器技术发展最前沿成果之一,是一款面向服务端应用的垃圾收集器。
G1可以说是CMS的终极改进版,解决了CMS内存碎片、更多的内存空间登问题。虽然流程与CMS比较相似,但底层的原理已是完全不同。
特点:
能充分利用多CPU、多核环境下的硬件优势;
可以并行来缩短(Stop The World)停顿时间;
也可以并发让垃圾收集与用户程序同时进行;
分代收集,收集范围包括新生代和老年代
能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;
能够采用不同方式处理不同时期的对象;
应用场景可以面向服务端应用,针对具有大内存、多处理器的机器;
采用标记-整理 + 复制算法来回收垃圾
使用方式:
//如何设置JVM参数底下会讲解:这里只是列举一部分参数: 设置垃圾收集器:"-XX:+UseG1GC":指定使用G1收集器; 设置垃圾收集器参数:"-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45; 设置垃圾收集器参数:"-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒; 设置垃圾收集器参数:"-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region
JVM内存参数简述
#常用的设置 -Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。 -Xmx:最大堆大小,JVM 运行过程中,如果初始堆空间不足的时候,最大可以扩展到多少。 -Xmn:设置堆中年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。 -XX:NewSize=n 设置年轻代初始化大小大小 -XX:MaxNewSize=n 设置年轻代最大值 -XX:NewRatio=n 设置年轻代和年老代的比值。如: -XX:NewRatio=3,表示年轻代与年老代比值为 1:3,年轻代占整个年轻代+年老代和的 1/4 -XX:SurvivorRatio=n 年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。8表示两个Survivor :eden=2:8 ,即一个Survivor占年轻代的1/10,默认就为8 -Xss:设置每个线程的堆栈大小。JDK5后每个线程 Java 栈大小为 1M,以前每个线程堆栈大小为 256K。 -XX:ThreadStackSize=n 线程堆栈大小 -XX:PermSize=n 设置持久代初始值 -XX:MaxPermSize=n 设置持久代大小 -XX:MaxTenuringThreshold=n 设置年轻带垃圾对象最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。 #下面是一些不常用的 -XX:LargePageSizeInBytes=n 设置堆内存的内存页大小 -XX:+UseFastAccessorMethods 优化原始类型的getter方法性能 -XX:+DisableExplicitGC 禁止在运行期显式地调用System.gc(),默认启用 -XX:+AggressiveOpts 是否启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,jdk6纸之后默认启动 -XX:+UseBiasedLocking 是否启用偏向锁,JDK6默认启用 -Xnoclassgc 是否禁用垃圾回收 -XX:+UseThreadPriorities 使用本地线程的优先级,默认启用
JVM的GC收集器设置:
-XX:+UseSerialGC:设置串行收集器,年轻带收集器 -XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。 -XX:+UseParallelGC:设置并行收集器,目标是目标是达到可控制的吞吐量 -XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。 -XX:+UseConcMarkSweepGC:设置年老代并发收集器 -XX:+UseG1GC:设置 G1 收集器,JDK1.9默认垃圾收集器