jvm堆内存及内存性能调优

一、守护线程与非守护线程的区别

java中两类线程:User Thread(用户线程)和Daemon Thread(守护线程)。 任何一个守护线程都会守护整个JVM中所有的非守护线程,只要当前JVM中还有任何一个非守护线程没有结束,守护线程就全部工作,当所有的非守护线程全部结束后,守护线程也会随着JVM一同结束。守护线程最典型的应用就是GC(垃圾回收器)。

二、Java堆(Java Heap)和(Non-Heap)

1、Java堆(Java Heap):

  • 对于大多数应用来说,Java 堆是Java 虚拟机所管理的内存中最大的一块。
  • Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
  • Java堆中唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
  • Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配

①,但是随着JIT 编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标量替换
②优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。

  • Java堆是垃圾收集器管理的主要区域,因此很多时候也被称作“GC堆”(Garbage Collected Heap)。
  • 根据Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,

也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms 控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。

2、方法区(Method Area)——又名Non-Heap:

  • 方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java 堆区分开来。
  • 根据Java 虚拟机规范的规定, 当方法区无法满足内存分配需求时, 将抛出OutOfMemoryError 异常。

三、堆内存原理

1、JVM堆内存分为2块:永久空间和堆空间。

  • 永久即持久代(Permanent Generation),主要存放的是Java类定义信息,与垃圾收集器要收集的Java对象关系不大。
  • Heap = {Old + NEW = {Eden,from,to}},Old即年老代(Old Generation),New即年轻代(Young Generation)。年老代和年轻代的划分对垃圾收集影响比较大。
  • 堆内存 = 年轻代 + 年老代(Tenured Gen) + 永久代

  •  

    年轻代 = Eden space区(伊甸园) + 两个Survivor space区(From和Toz, 幸存者区)

 2、年轻代、老年代、持久代

年轻代

所有新生成的对象首先都是放在年轻代。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代一般分3个区,1个伊甸区,2个幸存者区(从到)。

大部分对象在伊甸区中生成。当伊甸区满时,还存活的对象将被复制到幸存者区(两个中的一个),当一个幸存者区满时,此区的存活对象将被复制到另外一个幸存者区,当另一个幸存者区也满了的时候,从前一个幸存者区复制过来的并且此时还存活的对象,将可能被复制到年老代。

2个幸存者区是对称的,没有先后关系,所以同一个幸存者区中可能同时存在从伊甸区复制过来对象,和从另一个幸存者区复制过来的对象;而复制到年老区的只有另一个从幸存者区过来的对象。而且,因为需要交换的原因,幸存者区至少有一个是空的。特殊的情况下,根据程序需要,幸存者区是可以配置为多个的(多于2个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

针对年轻代的垃圾回收即Young GC。

年老代

在年轻代中经历了Ñ次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。

针对年老代的垃圾回收即Full GC。

持久代

用于存放静态类型数据,如Java的的的类,方法等。持久代对垃圾回收没有显着影响。但是有些应用可能动态生成或调用一些类,例如休眠CGLIB等,在这种时候往往需要设置一个比较大的持久代空间来存放这些运行过程中动态增加的类型。

 

所以,当一组对象生成时,内存申请过程如下:

  1. JVM会试图为相关的Java的的的对象在年轻代的伊甸园区中初始化一块内存区域。
  2. 当伊甸区空间足够时,内存申请结束。否则执行下一步。
  3. JVM试图释放在伊甸园区中所有不活跃的对象(年轻的GC)。释放后若伊甸园空间仍然不足以放入新对象,JVM则试图将部分伊甸园区中活跃对象放入幸存者区。
  4. 幸存者区被用来作为伊甸园区及年老代的中间交换区域。当年老代空间足够时,幸存者区中存活了一定次数的对象会被移到年老代。
  5. 当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
  6. 完全GC后,若幸存者区及年老代仍然无法存放从伊甸区复制过来的对象,则会导致JVM无法在伊甸园区为新生成的对象申请内存,即出现“内存不足”。

 3、内存溢出OOM(“Out of Memory”)

OOM(“Out of Memory”)异常一般主要有如下2种原因:

1)年老代溢出

表现为:java.lang.OutOfMemoryError:Javaheapspace
这是最常见的情况,产生的原因可能是:设置的内存参数XMX过小或程序的内存泄露及使用不当问题。
例如循环上万次的字符串处理,创建上千万个对象,在一段代码内申请上百中号甚至上ģ的内存。还有的时候虽然不会报内存溢出,却会使系统不间断的垃圾回收,也无法处理其它请求。这种情况下除了检查程序,打印堆内存等方法排查,还可以借助一些内存分析工具,比如MAT就很不错。

2)持久代溢出

表现为:java.lang.OutOfMemoryError:PermGenspace
通常由于持久代设置过小,动态加载了大量的Java类而导致溢出,解决办法唯有将参数-XX:MaxPermSize参数参数参数调大(一般256米能满足绝大多数应用程序需求)将部分的Java的的类放到容器共享区(例如Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,而这些应用有大量的共享类库。

四、性能监控及调优

目的:防止出现OOM、解决OOM、减少Full GC出现的频率

1、设置堆内存大小,显示垃圾回收的详细信息

-Xms512M -Xmx512M -XX:+PrintGCDetails

 

 说明:当老年代内存满了后触发一次full GC

 2、怎样使对象进入老年代

第一种情况是当对象的年龄大小达到一定大小时就会就如老年代,对象的年龄是由对象经历GC的次数决定的,在新生代每次GC之后如果对象没有被回收则年龄加1,通过以下参数来设置最大年龄

-XX:MaxTenuring Threshod=15

第二种情况是大对象,JVM里面有一个参数可以设置对象的大小超过在指定的大小之后,直接晋升为老年代

-XX:PretenureSizeThreshold=80M

3、jvm常用参数

 1)标准参数

-verbose:class 打印每个class信息
-verbose:gc 打印每次gc信息

2)非标参数 -X

-Xloggc:filename 设置GC log文件的位置 -Xloggc:log/gc.log
-Xms大小 设置堆的初始化大小 -Xmx2048m =-XX:InitialHeapSize
-Xmx大小 设置堆的最大大小 -Xms1024m =-XX:MaxHeapSize 一般Xms=Xmx,防止扩容和缩容
-Xmn大小 设置年轻代大小(初始化和最大) -Xmn256m
-XX:NewSize -XX:MaxNewSize 分别指定年轻代的初始化和最大大小,建议年轻代占堆大小的1/4 ~ 1/2
-Xss大小 设置线程栈大小 -Xss1m =-XX:ThreadStackSize

疑问解答

-Xmn,-XX:NewSize / -XX:MaxNewSize,-XX:NewRatio 3组参数都可以影响年轻代的大小,混合使用的情况下,优先级是什么?
如下:
  1. 高优先级:-XX:新尺寸/ -XX:MaxNewSize 
  2. 中优先级:-Xmn(默认等效-Xmn = -XX:NewSize = -XX:MaxNewSize =?) 
  3. 低优先级:-XX:NewRatio 
推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定NewSize / MaxNewSIze,而且两者相等,适用于生产环境。-Xmn配合-Xms / -Xmx,即可将堆内存布局完成。
-Xmn参数是在JDK 1.4开始支持。

3)不稳定参数 -XX

-XX:ErrorFile=文件 设置错误日志路径 -XX:ErrorFile=./hs_err_pid%p.log %p为当前进程号
-XX:OnError=命令 错误发生时执行命令 -XX:OnError="gcore %p;dbx - %p"
-XX:OnOutOfMemoryError=命令 内存溢出时执行命令
-XX:MaxDirectMemorySize=size 设置直接内存最大值 -XX:MaxDirectMemorySize=100m 默认为0 当直接内存达到设置的最大值会FullGC
-XX:ObjectAlignmentInBytes=alignment 设置java对象的内存对齐,默认是8字节
-XX:ThreadStackSize 设置线程栈大小 -XX:ThreadStackSize=1m = -Xss
-XX:-UseBiasedLocking 禁用偏向锁 默认开启,不禁用 如果使用的是大量的没有竞争的同步,使用偏向锁会提升性能
-XX:-UseCompressedOops 禁用压缩指针堆内存小于32G时默认开启 开启后,对象引用是32位而不是64位,可以提升性能。只有64位的jvm才生效
-XX:+DoEscapeAnalysis 开启逃逸分析 默认开启
-XX:+Inline 开启方法内联 默认开启
-XX:InlineSmallCode=大小 设置应内联的已编译方法的最大代码大小,只有小于此数值的才会内联 默认1000字节
-XX:MaxInlineSize=大小 设置要内联方法的最大字节码大小 默认35字节
-XX:+OptimizeStringConcat 开启字符串连接优化 默认开启
-XX:+PrintInlining 打印方法内联 默认关闭,需和-XX:+UnlockDiagnosticVMOptions 一起使用
-XX:-TieredCompilation 关闭分层编译 默认开启
-XX:+HeapDumpOnOutOfMemoryError  OOM时堆内存dump到当前目录
-XX:HeapDumpPath=路径 设置堆内存dump的路径 -XX:HeapDumpPath=/var/java_pid%p.hprof
-XX:+UnlockDiagnosticVMOptions 开启jvm诊断功能选项

垃圾回收相关参数
-XX:+AggressiveHeap 开启堆最优化设置 默认关闭
-XX:+CMSClassUnloadingEnabled 当使用CMS垃圾收集器时,允许类卸载 默认开启
-XX:CMSExpAvgFactor=percent 指定垃圾收集消耗的时间百分比 默认这个数是25%,就是25
-XX:CMSInitiatingOccupancyFraction=percent 设置CMS回收开始的老年代百分比 默认-1,任何的负值表示会使用-XX:CMSTriggerRatio选项来定义这个百分比数
-XX:+CMSScavengeBeforeRemark 在CMS重新标记之前执行ygc操作  默认关闭 在remark时间过长时可以开启;开启减少remark的STW时间
-XX:CMSTriggerRatio=percent 设置CMS开始的百分比 默认80,((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio * MinHeapFreeRatio) / 100.0) / 100.0=92%
-XX:+UseCMSCompactAtFullCollection  在FULL GC的时候,对年老代的压缩
-XX:CMSFullGCsBeforeCompaction=0  上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩,
-XX:MinHeapFreeRatio=percent GC之后堆内存最小剩余百分比,如果小于此值,则自动扩容 默认40%
-XX:MaxHeapFreeRatio=percent GC之后堆内存最大剩余百分比,如果小于此值,则自动缩容 默认70%
-XX:ParallelGCThreads=threads 设置Parallel GC的线程数 默认根据cpu个数,<=8,则使用8个,>8个3+5N/81台服务器只有1个jvm时使用默认值较好,如果有n个jvm,cpu个数 / n比较合适
-XX:ConcGCThreads=个数 并发GC的线程数 默认值取决于cpu个数 ConcGCThreads = (ParallelGCThreads + 3)/4
-XX:+DisableExplicitGC 使System.gc()显式gc失效 默认不开启,
-XX:G1HeapRegionSize=size 当使用G1收集器时,设置java堆被分割的region大小 1M~32M默认根据堆内存最优化设置
-XX:+G1PrintHeapRegions 打印G1收集器收集的区域  默认关闭
-XX:G1ReservePercent=percent  设置堆内存保留大小,以防晋升失败 0~50 默认10%
-XX:InitialHeapSize=size 堆初始大小 =-Xms
-XX:MaxHeapSize=size 堆最大大小 =-Xmx
-XX:InitialSurvivorRatio=ratio 设置伊甸园区和幸存区初始比例 默认为8:1
-XX:SurvivorRatio=ratio 设置伊甸园区和幸存区比例 默认为8:1  8:1:1
XX:TargetSurvivorRatio YGC之后,幸存区期望百分比 默认 50%
XX:InitiatingHeapOccupancyPercent=percent 堆占用达到多少开始并发垃圾回收 只有并发垃圾回收器生效
-XX:MaxGCPauseMillis=time GC最大暂停时间 ms 默认没有最大暂停时间
-XX:MetaspaceSize=size 元空间多次扩容后超过此值就会full gc 默认大约20M 元空间使用本地内存。 尽量设置足够,避免因为这个区域内存不够引发Full GC
-XX:MaxMetaspaceSize=size 设置元空间最大大小 默认不限制 建议和MetaspaceSize一样大,一般256M
-XX:NewSize=size 设置年轻代初始大小 建议年轻代占堆大小的1/4 ~ 1/2
-XX:MaxNewSize=size 设置年轻代最大大小 默认根据最大效能分配
-XX:MaxTenuringThreshold=threshold 最大晋升年龄,从年轻代到老年代 默认:15 - 并行回收器 6 - CMS
-XX:NewRatio=ratio 设置老年代和新生代比例 默认2 老年代 : (伊甸园 + 2个幸存区)
-XX:+PrintGC 打印GC信息 默认关闭
-XX:+PrintGCDetails 打印GC详细信息 默认关闭
-XX:+PrintTenuringDistribution 打印晋升分配
-XX:+ScavengeBeforeFullGC Full gc之前先ygc 默认开启
-XX:+UseTLAB 年轻代使用线程局部缓存 默认开启  效率高
-XX:TLABSize=size 设置初始化thread-local allocation buffer (TLAB)大小
-XX:+UseAdaptiveSizePolicy  JDK 1.8 默认使用 UseParallelGC 垃圾回收器,该垃圾回收器默认启动了 AdaptiveSizePolicy
-XX:+UseParallelGC 年轻代使用并行回收器
-XX:+UseParallelOldGC 老年代使用并行回收器
-XX:+UseParNewGC 为配置CMS,年轻代使用ParNew回收器,CMS会默认开启
-XX:+UseConcMarkSweepGC 使用CMS回收器
-XX:+UseG1GC 使用G1回收器
-XX:+UseGCOverheadLimit 限制GC的运行时间,通过统计GC时间来预测是否要OOM了,提前抛出异常,防止OOM发生 并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作
-XX:+UseStringDeduplication 开启字符串去重 G1回收器生效
-XX:AutoBoxCacheMax=20000 加大Integer Cache
-XX:+PrintPromotionFailure 知道是多大的新生代对象晋升到老生代失败从而引发Full GC时的。

其他:

-Xmx3550m:设置JVM 最大堆内存为3550M(或 -XX:MaxHeapSize=size)
-Xms3550m:设置JVM 初始堆内存为3550M此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xss128k:。设置每个线程的栈大小.JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K应当根据应用的线程所需内存大小进行调整在相同物理内存下,。减小这个值能生成更多的线程但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右需要注意的是:当这个值被设置的较大(例如> 2MB)时将会在很大程度上降低系统的性能。
-Xmn2g:设置年轻代。大小为2G在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。(或 -XX:NewSize=size / -XX:MaxNewSize=size )
-XX:新尺寸= 1024米:设置年轻代初始值为1024M。
-XX:MaxNewSize = 1024米:设置年轻代最大值为1024M。
-XX:PermSize = 256米:设置持久代初始值为256M。
-XX:MaxPermSize参数参数= 256米:设置持久代最大值为256M。
-XX:NewRatio = 4:设置年轻代(包括1个伊甸和2个幸存者区)与年老代的比值表示年轻代比年老代为1:4-XX:SurvivorRatio = 4:设置年轻代中伊甸区与幸存者区的比值。表示2个幸存者区(JVM堆内存年轻代中默认有2个大小相等的幸存者区)与1个伊甸区的比值为2:4,即1个幸存者区占整个年轻代大小的1/6-XX:MaxTenuringThreshold = 7:表示一个对象如果在幸存者区(救助空间)移动了7次还没有被垃圾回收就进入年老代如果设置为0的话,则年轻代对象不经过幸存者区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在幸存者区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
-XX:PrintFlagsInitial :查看所有参数的默认初始值
-XX:+PrintFlagsFinal :查看所有参数的最终值

4、垃圾回收器选择

JVM给出了3种选择:串行收集器,并行收集器,并发收集器串行收集器只适用于小数据量的情况,所以生产环境的选择主要是并行收集器和并发收集器。

默认情况下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数.JDK5.0以后,JVM会根据当前系统配置进行智能判断。

串行收集器

  • -XX:+ UseSerialGC:设置串行收集器。

并行收集器(吞吐量优先)

  • -XX:+ UseParallelGC:。设置为并行收集器此配置仅对年轻代有效即年轻代使用并行收集,而年老代仍使用串行收集。
  • -XX:ParallelGCThreads = 20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收此值建议配置与CPU数目相等。
  • -XX:+ UseParallelOldGC:配置年老代垃圾收集方式为并行收集.JDK6.0开始支持对年老代并行收集。
  • -XX:MaxGCPauseMillis = 100:设置每次年轻代代垃圾回收的最长时间(单位毫秒)如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
  • -XX:+ UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代伊甸区大小和幸存者区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标此参数建议在使用并行收集器时,一直打开。

并发收集器(响应时间优先)

  • -XX:+ UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集的.cms收集是JDK1.4后期版本开始引入的新GC算法它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象的的的.cms收集的目标是尽量减少应用的暂停时间,减少全GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
  • -XX:+ UseParNewGC:设置年轻代为并发收集可与CMS收集同时使用.JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此参数。
  • -XX:CMSFullGCsBeforeCompaction = 0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
  • -XX:+ UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
  • -XX:+ CMSIncrementalMode:设置为增量收集模式一般适用于单CPU情况。
  • -XX:CMSInitiatingOccupancyFraction = 70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年代代的对象,避免Full GC的发生。

其它垃圾回收参数

  • -XX:+ ScavengeBeforeFullGC:年轻代GC优于全GC执行。
  • -XX:-DisableExplicitGC:不响应System.gc()的的的代码。
  • -XX:+ UseThreadPriorities:启用本地线程优先级API即使生效,不启用  java.lang.Thread.setPriority() 则无效。
  • -XX:SoftRefLRUPolicyMSPerMB = 0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
  • -XX:TargetSurvivorRatio = 90:允许90%的幸存者区被占用(JVM默认为50%)提高对于幸存者区的使用率。

辅助信息参数设置

  • -XX:-CITime:打印消耗在JIT编译的时间。
  • -XX:错误文件= / hs_err_pid.log:保存错误日志或数据到指定文件中。
  • -XX:HeapDumpPath = / java_pid.hprof:指定转储堆内存时的路径。
  • -XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时卸出此时的堆内存。
  • -XX:的OnError =“;”:出现致命错误后运行自定义命令。
  • -XX:OnOutOfMemoryError =“;”:当首次遭遇内存溢出时执行自定义命令。
  • -XX:-PrintClassHistogram:按下Ctrl + Break后打印堆内存中类实例的柱状信息,同JDK的jmap -histo命令。
  • -XX:-PrintConcurrentLocks:按下Ctrl + Break后打印线程栈中并发锁的相关信息,同JDK的jstack -l命令。
  • -XX:-PrintCompilation:当一个方法被编译时打印相关信息。
  • -XX:-PrintGC:每次GC时打印相关信息。
  • -XX:-PrintGCDetails:每次GC时打印详细信息。
  • -XX:-PrintGCTimeStamps:打印每次GC的时间戳。
  • -XX:-TraceClassLoading:跟踪类的加载信息。
  • -XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
  • -XX:-TraceClassResolution:跟踪常量池。
  • -XX:-TraceClassUnloading:跟踪类的卸载信息。

垃圾回收参数列表

  • -XX:+PrintGC 输出GC日志
  • -XX:+P[rintGCDetails 输出GC的详细日志
  • -XX:+PrintGCTimeStamps 输出GC的时间戳
  • -XX:+PrintGCDateStamps 输出GC的时间戳
  • -XX:+PrintHeapAtGC 在GC前后打印出堆的信息
  • -Xloggc:../logs/gc.log 日志文件的输出路径

关于参数名称等

  • 标准参数( - ),所有JVM都必须支持这些参数的功能,而且向后兼容;例如:
    • -client - 设置JVM使用客户端模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或开发调试;在32位环境下直接运行Java程序默认启用该模式。
    • -server - 设置JVM使服务器模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有64位能力的JDK环境下默认启用该模式。
  • 非标准参数(-X),默认JVM实现这些参数的功能,但是并不保证所有JVM实现都满足,且不保证向后兼容;
  • 非稳定参数(-XX),此类参数各个JVM实现会有所不同,将来可能会不被支持,需要慎重使用;

补充:G1GC

G1GC
G1 GC是启发式算法,会动态调整年轻代的空间大小。目标也就是为了达到接近预期的暂停时间。
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。

Young GC
Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。

Mixed GC
Mix GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。
它的GC步骤分2步: 全局并发标记(global concurrent marking) 拷贝存活对象(evacuation)
在进行Mix GC之前,会先进行global concurrent marking(全局并发标记)。 global concurrent marking的执行过程是怎样的呢?
在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。
global concurrent marking的执行过程分为五个步骤:

初始标记(initial mark,STW)在此阶段,G1 GC 对根对象进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。
根区域扫描(root region scan)G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。
并发标记(Concurrent Marking)G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断
最终标记(Remark,STW)该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。
清除垃圾(Cleanup,STW)在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。
Young GC:选定所有新生代里的region。通过控制新生代的region个数来控制young GC的开销。
Mixed GC:选定所有新生代里的region,外加根据global concurrent marking统计得出收集收益高的若干老年代region。在用户指定的开销目标范围内尽可能选择收益高的老年代region。

在G1中,有一种特殊的区域,叫Humongous区域。 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在年老代。

5、JVM服务参数调优实战

 大型网站服务器案例:

  • 承受海量访问得动态云服务应用
  • 服务器配置:8 CPU,8G 内存,JDK 1.8.X

参数方案:

-server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio = 6 -XX:MaxPermSize = 256m -XX:ParallelGCThreads = 8 -XX:MaxTenuringThreshold = 0 -XX:+ UseConcMarkSweepGC

#附件使用G1GC
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=4M
-XX:MaxTenuringThreshold=12
-XX:InitiatingHeapOccupancyPercent=40
-XX:ConcGCThreads=4 当前核心数的一半
-XX:+UseStringDeduplication
-XX:AutoBoxCacheMax=20000
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-XX:+PrintPromotionFailure
-XX:+HeapDumpOnOutOfMemoryErro
-XX:HeapDumpPath=${LOGDIR}/java_pid${pid}.hprof
-Xloggc:/data/log/gc-myapp.log 
-verbose:gc -XX:+PrintGCDateStamps -XX:+PrintGCDetails
-XX:+DisableExplicitGC 

调优说明:

  • -Xmx与-Xms相同以避免JVM反复重新申请内存。-XMX的大小约等于系统内存大小的一半,即充分利用系统资源,又给予系统安全运行的空间。
  • -Xmn1256m设置年轻代大小为1256MB。此值对系统性能影响较大,太阳官方推荐配置年轻代大小为整个堆的3/8。
  • -Xss128k设置较小的线程栈以支持创建更多的线程,支持海量访问,并提升系统性能。
  • -XX:SurvivorRatio = 6设置年轻代中Eden区与Survivor区的比值。系统默认是8,根据经验设置为6,则2个幸存者区与1个Eden区的比值为2:6,一个幸存者区占整个年轻代的1/8。
  • -XX:ParallelGCThreads = 8配置并行收集器的线程数,即同时8个线程一起进行垃圾回收。此值一般配置为与CPU数目相等。
  • -XX:MaxTenuringThreshold = 0设置垃圾最大年龄(在年轻代的存活次数)。如果设置为0的话,则年轻代对象不经过Survivor区直接进入年老代。对于年老代比较多的应用,可以提高效率;如果将此值设置为一个较大值,则年轻代对象会在幸存者区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率根据被海量访问的动态网络应用之特点,其内存要么被缓存起来以减少直接访问数据库,要么被快速回收以支持高并发海量请求,因此其内存对象在年轻代存活多次意义不大,可以直接进入年老代,根据实际应用效果,在这里设置此值为0。
  • -XX:+ UseConcMarkSweepGC设置年老代为并发收集.CMS(ConcMarkSweepGC)收集的目标是尽量减少应用的暂停时间,减少完全GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存,适用于应用中存在比较多的长生命周期对象的情况。

内部集成构建服务器案例:

  • 高性能数据处理的工具应用
  • 服务器配置:1个CPU,4G MEM,JDK 1.8.X

参数方案:

-server -XX:PermSize = 196m -XX:MaxPermSize = 196m -Xmn320m -Xms768m -Xmx1024m

调优说明:

  • -XX:PermSize = 196m -XX:MaxPermSize = 196m根据集成构建的特点,大规模的系统编译可能需要加载大量的Java类到内存中,所以预先分配好大量的持久代内存是高效和必要的。
  • -Xmn320m遵循年轻代大小为整个堆的3/8原则。
  • -Xms768m -Xmx1024m根据系统大致能够承受的堆内存大小设置即可

posted on 2021-10-29 17:44  uestc2007  阅读(365)  评论(0编辑  收藏  举报

导航