JVM内存参数

  • Xms :java堆内存大小
  • Xmx :java堆内存的最大大小
  • Xmn:java堆内存中新生代的大小,扣除新生代,剩下的就是老年代
  • XX:PermSize : 永久代大小 (1.8移除了)
  • XX:MaxPermSize : 永久代最大大小
  • Xss : 每个线程栈内存大小
  • XX:SurvivorRatio=8 模式是Eden区域比例为80%
  • XX:MaxTenuringThreshold=15 新生代躲过多少集回收后进入老年代
  • -XX:PretenureSizeThreshold=1M 大对象的大小,超过进入老年代
  • -XX:+UseParNewGC 使用parNew 处理老年代gc
  • -XX:+CMSFullGCsBeforeCompaction=0 full gc 后老年代多少次整理一次老年代内存空间
  • -XX:HanderPromotionFailure. 开启分配担保机制
  • -Xms=20M -Xmx=20M -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -Xloggc:../logs/gc.log

参数解释

  • -Xms1024 | -XX:InitialHeapSize=1024M 堆初始化大小
  • -Xmx1024 | -XX:MaxHeapSize=1024M 堆最大大小
  • -Xmn500 | -XX:NewSize=500M 新生代大小
  • -XX:MaxNewSize=500 新生代最大值
  • -Xss1M |ThreadStackSize=1M 每个线程大小
  • -XX:+UseParNewGC
  • -XX:+UseConcMarkSweepGC
  • -XX:+CMSFullGCsBeforeCompaction=5 每次进行垃圾回收执行一次压缩,解决碎片问题 (1.8后移除)
  • -XX:+CMSParallellnitialMarkEnabled 在初始标记阶段开启多线程并发执行
  • -XX:CMSInitiatingOccupancyFraction=68 老年代使用多少进major gc
  • -XX:+CMSScavengeBeforeRemark CMS重新标记阶段,先执行一次Young GC
  • -XX:MetaspaceSize 指定元空间大小
  • -XX:TraceClassLoading. 在catalina.out 中输出jvm类加载
  • -XX:TraceClassUnloading 在catalina.out 中输出jvm类卸载
  • -XX:DisableexplicitGC. 关闭system.gc()执行full gc
  • -XX:+CMSParallelInitialMarkEnabled表示在初始标记的多线程执行,减少STW;
  • -XX:+CMSScavengeBeforeRemark:在重新标记之前执行minorGC减少重新标记时间;
  • -XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,降低STW;
  • -XX:CMSInitiatingOccupancyFraction=92和-XX:+UseCMSInitiatingOccupancyOnly配套使用,如果不设置后者,jvm第一 次会采用92%但是后续jvm会根据运行时采集的数据来进行GC周期,如果设置后者则jvm每次都会在92%的时候进行gc; -XX:+PrintHeapAtGC:在每次GC前都要GC堆的概况输出

-Xms3072M -Xmx3072 -Xmn1536M -Xss1M -XX:PernSize=256M -XX:MaxPermSize=256M
image.png

  • 订单系统的系统程序在大促期间不停的运转,每秒处理300个订单,都会占据新生代60MB空间,但是1.5g 内存空间25s就会被占满,25s过后就要进行Minor GC了,此时因为有 -XX:HandleePromotionFailure (空间担保)选项,所以可以认为需要进行检查,主要是比较 “老年代可用空间大小”和历次Minorgc后进入老年代对象的平均大小,刚开始肯定这个检查是可以通过的,所以Minor gc,一下子后收掉了99%的新生代对象,因为除了最近1s的订单请求还在处理,大部分订单早就处理完了,此时可能存活对象就100MB,但是,如果-XX:SurvivorRatio 参数默认值为8,那么此时新生代里Eden区大概占据了1.2gb内存,剩余每个Survivor区域是150mb内存。所以Eden区1.2g就满了,要进行Minor GC,因此大概只需要20s,就会把Eden区塞的满满的,然后100MB左右的对象会放入S1区域内。

image.png
此时JVM参数如下
-Xms3072M -Xmx3072M -Xmn1536 -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8

新生代垃圾回收优化

Survivor 空间够不够 :首先在进行JVM 优化的时候,第一个要考虑的问题就是你通过估算,你的新生代Survivor区到底够不够,按照上述逻辑,首先每次新生代回收垃圾在100mb 左右,有可能会突破150mb,那么岂不是会经常出现MinorGC 过后的对象无法放入survivor中,然后频繁的会让对象进入老年代?
其次,即使是Minor GC后的对象少于150mb,但是因为是一批同年龄对象,直接超过了Survivor区空间的百分之50,此时也有可能会导致对象进入老年代。

这里考虑降低老年代空间,因为这种普通业务系统大部分对象都是短生命周期,根本不应该频繁进入老年代,没必要给老年代位置过大的内存空间,尽量让对象留在新生代里面,因此考虑2g 新生代 1g老年代 此时eden为1.6g survivor为200mb,这样就大大的降低了新生代对象进入老年代的概率

image.png

此时jvm参数如下;
-Xms3072M -Xmx3072M -Xmn2048 -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8
-Xmn 堆内存中新生代的大小
-XX:MaxTenuringThreshold=5 设置躲过几次回收后进入老年代

指定垃圾回收器

新生代使用ParNew, 老年代使用cms 如下:
-Xms3072M -Xmx3072 -Xss1M -Xmn2048 -XX:PermSize=256M -XX:MaxPermSize=256M -XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M —XX:+UseParNewGC -XX:+UseConcMarkSweepGC

ParNew Garbage 垃圾回收器的核心参数,其实就是配套的新生代内存大小、Eden和Survivor的比例,只要你设置合理,避免Minor GC后对象放不下Survivor 进入老年代,或者动态判定之后进入老年代,给新生代的Survivor充足的空间,那么MinorGC 一般就没什么问题,然后根据你的新系统运行模型,合理的设置-XX:MaxTenuringThreshold 让那些长期存活的对象,抓紧尽快进入老年代,别再新生代里一直待着。
其实对很多普通的Java系统而言,只要对系统运行期间的内存使用模型做好预估,然后分配好合理的内存空间,尽量 让Minor GC之后的存活对象留在Survivor里不要去老年代,然后其余的GC参数不做太多优化,系统性能基本上就不会太差。

快照

jmap 命令用来生成内存堆转储快照,一般称为heapdump或dump文件。
除了使用 jmap 命令,还以通过一些JVM参数让虚拟机在内存溢出时自动dump出快照文件。

参数 说明
-XX:+HeapDumpOnOutOfMemoryError 内存溢出时自动导出内存快照
-XX:HeapDumpPath=E:/dumps/ 导出内存快照时保存的路径
  • jmap [ options ] pid
  • jmap [ options ] executable core
  • jmap [ options ] [ pid ] server-id@ ] remote-hostname-or-IP

jmap -dump#
jmap 命令使用-dump参数生成内存快照,-dump参数格式如下:
-dump:[live,] format=b, file=filename
举例如下:jmap -dump:format=b,file=/dumps/jmap.hprof 21060