JVM 调优

Stella981
• 阅读 687

JVM 堆内存分区

堆:年轻代,老年代,持久代

年轻代:Eden,Survivor1,Survivor2

JVM 调优

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基本回收算法

  1. 引用计数(Reference Counting)
    比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。

  2. 标记-清除(Mark-Sweep)
    此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。

  3. 复制(Copying)
    此 算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理 正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不过出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍 内存空间。

  4. 标记-整理(Mark-Compact)
    此算法结 合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活 对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

  5. 增量收集(Incremental Collecting)
    实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。

  6. 分代(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"

调优总结

  1. 年轻代大小选择

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

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

  2. 年老代大小选择

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

      • 并发垃圾收集信息

      • 持久代并发收集次数

      • 传统GC信息

      • 花在年轻代和年老代回收上的时间比例

    • 减少年轻代和年老代花费的时间,一般会提高应用的效率

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

    • CMS调优可针对老年代

  3. 较小堆引起的碎片问题

因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

一个性能较好的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
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
秋招面试我去了拼多多,直接被问JVM&GC底层原理和算法,我吊打面试官
(https://shimo.im/docs/9GTP6XrJg9J88cJD/)JVM常用参数设置积累c堆的初始值,默认物理内存的1/64Xms:堆的最大值,默认物理内存的1/4Xmx:年轻代大小「在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8」X
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Java虚拟机堆内存(新生代)
Java中的堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实例对象。在Java中,堆被划分成两个不同的区域:新生代,老年代。新生代又被分为了三个区域:Eden,from survivor,tosurvivor。这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包括内存分配以及回收。堆的内存模型大致为:从图中可以看出:堆大
Wesley13 Wesley13
3年前
Java工程师成神之路
一、基础篇1.1JVM1.1.1.Java内存模型,Java内存管理,Java堆和栈,垃圾回收http://www.jcp.org/en/jsr/detail?id133http://ifeve.com/jmmfaq/1.1.2.了解JVM各种参数及调优
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
3年前
JVM基本配置与调优
JVM基本配置与调优JVM调优,一般都是针对堆内存配置调优。如图:堆内存分新生代和老年代,新生代又划分为eden区、from区、to区。!(https://oscimg.oschina.net/oscnet/5b30ad2de1851315e10f5d9e0a57395208c.png)一、区域释义
Stella981 Stella981
3年前
JVM笔记九
在上一篇文章中,我们通过代码运行结果,查看到JVM的堆内存逻辑上分区是三部分,物理上分区是2部分,以及是新生代分区三部分,占比分布是8/1/1。而且我们还通过代码和堆JVM参数配置,制造出了OOM异常。下面我们就来分析GC回收器的日志信息。先来看看,OOM后,GC详细日志信息:!dd604a3c4cda17304edcc43b03106d58.pn
Stella981 Stella981
3年前
JVM 参数学习
一、JVM1、JVM产生GC的位置Eden(新生代)MinorGC算法(复制)Oldtenure(老年代) Major(Full)GC(整理压缩)算法2、JVM堆(Heap)内存大小参数\Xmn新生代8:1:1比例\Xms设置初始化堆内存大小 \Xmx设置堆内存最大大小 产生java.lang.
Stella981 Stella981
3年前
JVM系列【6】GC与调优6
JVM系列笔记目录虚拟机的基础概念class文件结构class文件加载过程jvm内存模型JVM常用指令GC与调优GC常用参数\XmnXmsXmxXss年轻代最小堆最大堆栈空间\XX:UseTLAB使用