JVM 堆内存分区
堆:年轻代,老年代,持久代
年轻代:Eden,Survivor1,Survivor2
GC
- 新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。 - 老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常
会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里
就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10
倍以上。 - 持久代,在Java8中已经被替换成元空间 MetaSpace。
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。
JVM基本回收算法
引用计数(Reference Counting)
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。标记-清除(Mark-Sweep)
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。复制(Copying)
此 算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理 正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不过出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍 内存空间。标记-整理(Mark-Compact)
此算法结 合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活 对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。增量收集(Incremental Collecting)
实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。分代(Generational Collecting)
基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
JVM常见配置汇总
1.堆设置
-Xms 初始堆大小(默认值,物理内存的1/64)
-Xmx 最大堆大小(默认值,物理内存的1/4)
-Xmn 年轻代大小(Sun官方推荐年轻代配置为整个堆的3/8) 。【使用此参数相当于同时使用-XX:NewSize=n和-XX:MaxNewSize=n】
-XX:NewSize=n 设置年轻代大小
-XX:MaxNewSize=n 设置年轻代最大值
-XX:NewRatio=n 设置老年代和年轻代的比值。如:为3,表示年老代与年轻代比值为3:1,年轻代占整个年轻代年老代和的1/4。
-XX:SurvivorRatio=n 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-Xss 每个线程的堆栈大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.
-XX:+UseBiasedLocking 锁机制性能改善
-XX:PermSize=n 设置持久代的大小 (物理内存的1/64)
-XX:MaxPermSize=n 设置持久代大小最大值(物理内存的1/4 JDK 7)
-XX:MetaSpaceSize=n 设置元空间大小(JDK 8)
-XX:MaxMetaspaceSize=n 设置元空间最大值(JDK 8)
2.JDK1.8持久代被元空间替代
设置元空间大小元空间使用的是本地内存而并非JVM虚拟机内存,当需要控制时,可以采用配置项来控制元空间大小。
在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。
-XX:MaxMetaspaceSize=128m
3.收集器
G1收集器
G1收集器是基于标记-压缩算法的,因此,它不会产生空间碎片,也没必要再收集完成之后进行一次独占式的碎片整理工作。G1收集器还可以进行非常精确的停顿控制,他可以让开发人员指定在长度为M的时间段中,来及回收时间不超过N
要启用 G1 收集器请使用: -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions
设置G1回收器的目标停顿时间:
-XX:MaxGCPauseMillis = 50 -XX:GCPauseIntervalMillis=200
以上参数 指定在200ms内,停顿时间不超过50ms。这两个参数是G1回收器的目标,G1回收器并不保证能执行它们。
参数
含义
-XX:G1HeapRegionSize=n
设置Region大小,并非最终值
-XX:MaxGCPauseMillis
设置G1收集过程目标时间,默认值200ms,不是硬性条件
-XX:G1NewSizePercent
年轻代最小值,默认值5%
-XX:G1MaxNewSizePercent
新生代最大值,默认值60%
-XX:ParallelGCThreads
STW期间,并行GC线程数
-XX:ConcGCThreads=n
并发标记阶段,并行执行的线程数
-XX:InitiatingHeapOccupancyPercent
设置触发标记周期的 Java 堆占用率阈值。默认值是45%。这里的java堆占比指的是non_young_capacity_bytes,包括old+humongous
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器 (此配置仅对年轻代有效)
-XX:+UseParallelOldGC :设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器(参数表示对于老年代的回收采用CMS。CMS采用的基础算法是:标记—清除。)
(-XX:+<.....>启动某个选项,-XX:-<....>不启用某个选项)
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
- -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
- -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
- -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
常见配置:
请看一下一个时间的Java参数配置:(服务器:Linux 64Bit,8Core×16G)
export JAVA_OPTS="$JAVA_OPTS -server -Xms4G -Xmx4G -Xss256k
-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:+UseParallelOldGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/aaa/dump
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/usr/aaa/dump/heap_trace.txt
-XX:NewSize=1G
-XX:MaxNewSize=1G"
调优总结
年轻代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息
持久代并发收集次数
传统GC信息
花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
CMS调优可针对老年代
较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
一个性能较好的web服务器jvm参数配置:
-server //服务器模式
//使用并发收集器时,开启对年老代的压缩。
-XX:+UseCMSCompactAtFullCollection
//上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
-XX:CMSFullGCsBeforeCompaction=0
-Xmx2g //JVM最大允许分配的堆内存,按需分配
-Xms2g //JVM初始分配的堆内存,一般和Xmx配置成一样以避免每次gc后JVM重新分配内存。
-Xmn256m //年轻代内存大小,整个JVM内存=年轻代 + 年老代 + 持久代
-XX:MaxMetaspaceSize=128m //持久代内存大小
-Xss256k //设置每个线程的堆栈大小
-XX:+DisableExplicitGC //忽略手动调用GC, System.gc()的调用就会变成一个空调用,完全不触发GC
-XX:+UseConcMarkSweepGC //并发标记清除(CMS)收集器
-XX:+CMSParallelRemarkEnabled //降低标记停顿
-XX:+UseCMSCompactAtFullCollection //在FULL GC的时候对年老代的压缩
-XX:LargePageSizeInBytes=128m //内存页的大小
-XX:+UseFastAccessorMethods //原始类型的快速优化
-XX:+UseCMSInitiatingOccupancyOnly //使用手动定义初始化定义开始CMS收集
-XX:CMSInitiatingOccupancyFraction=70 //使用cms作为垃圾回收使用70%后开始CMS收集
Full GC
Full GC一般容易出现stop-the-world现象,出现在实践中时 (经常会出现在生产系统中),经常被证实是老年代有大量不必要的对象。一个可行的办法就是增加年轻代的堆大小,以防止年轻代短生命的对象提前进入老年代。另一个办法就似乎利用分析器,快照运行系统的堆转储,并且分析过度的对象分配,找出这些对象,最终减少这些对象的申请。
常用的JVM调优方案
1. 将新对象预留在新生代
一般来说,当survivor区空间不够,或者占用量达到50%时,就会将对象进入老年代(不管对象年龄有多大)。
由于Full GC的成本要远远高于Minor GC,因此尽可能的将对象分配在新生代是一项明智的做法,在JVM调优中,可以为应用程序分配一个合理的新生代空间,以最大限度的避免新对象直接进入老年代的情况。
-XX:NewRatio=n 可以设置新生代的大小比例
当对象占用年轻代里的from区的一半时,对象会被直接送入老年代 。
解决办法是
通过设置 -XX:TargetSurvivorRatio=95 增大from区的利用率,使用from 区到95%是才将对象送入老年代。
通过设置 -XX:SurvivorRatio=n 设置更大from区空间比例。
2. 大对象进入老年代
开发中要避免短命的大对象,目前没有特别好的方法回收短命大对象,大对象最好直接进入老年区,因为大对象在新生区,占用空间大,会由于空间不足而导致很多小对象进入到老年区。
-XX:PretenureSizeThreshold:设置大对象直接进入老年代的阈值,当对象的大小超过这个值将直接分配在老年代.
当创建的对象超过指定大小时,直接把对象分配在老年代中。
-XX:PretenureSizeThreshold=3145728 参数设定超过对象超过多少时,分配到老年代中,此例为3M(3*1024*1024)。
3. 设置对象进入老年代 的年龄
如果对象在eden区,经过一次GC后还存活,则被移动到survivior区中,对象年龄增加1,以后没经过一次GC,对象还存活,则年龄加1,当对象年龄达到阈值后,就移入老年代,成为老年对象。
-XX:MaxTenuringThreshold=n,设置对象进入老年代的年龄,默认值时15,但是如果空间不够,还是会将对象移到老年代。推荐30.
4. 稳定与震荡的堆大小
稳定的堆大小能减少gc次数,但是每次gc时间增加
震荡的堆大小能增加gc次数,但是每次gc时间减少
-XX:MinHeapFreeRatio 最小空闲比例,当堆空间空闲内存小于这个比例,则扩展
-XX:ManHeapFreeRatio 最大空闲比例,当堆空间空闲内存大于这个比例,则压缩
-Xms和-Xmx相等时,上面的参数失效
5. 吞吐量方案
设置 最小堆和最大堆的值一样,防止内存震荡, -Xmx4g -Xms4g一样。
4G内存32核吞吐量优先方案:尽可能减少系统的执行垃圾回收的总时间,考虑使用关注吞吐量的并行回收收集器。
-Xms3800m
-Xmx3800m
-Xss128k //减少线程栈大小,使剩余系统内存支持更多线程
-Xmn2g //设置新生代大小
-XX:UseParallelGC 新生代并行回收收集器
-XX:ParallelGCTHreads 设置线程数
-XX:+UseParallelOldGC 老年代也使用并行回收收集器
6. 使用大页案例
-Xmx2506m
-Xms2506m
-Xmn1536m
-Xss128k //减少线程栈大小,使剩余系统内存支持更多线程
-XX:UseParallelGC 新生代并行回收收集器
-XX:ParallelGCTHreads=20
-XX:+UseParallelOldGC 老年代也使用并行回收收集器
-XX:LargePageSizeInBytes=256m //设置大页的大小
7. 降低停顿案例
降低停顿首先考虑的是使用关注系统停顿的cms回收器,其次为了减少full gc次数,应尽可能将对象预留在新生代,因为新生代minor gc的成本远小于老年代的full gc
-Xms3550m
-Xmx3550m
-Xss128k //减少线程栈大小,使剩余系统内存支持更多线程
-Xmn2g //设置新生代大小
-XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC //老年代使用cms回收器
-XX:+UseParNewGC //新生代使用并行回收器
-XX:SurvivorRatio=8 //设置eden和survivor比例为8:1
-XX:TargetSurvivorRatio=90 //设置survivor使用率
-XX:MaxTenuringThreshold=31 //年轻对象进入老年代的年龄,默认是15,这里是31