jvm-gc

gc简述

gc分为minor gc和full gc,区别在于前者指回收新生代(1个Eden和2个Survivor),后者通常会先进行一次minor gc然后再full gc,full gc回收老年代(Tenure),顺带回收永久代(Perm,jdk8中被Metadata取代,Metadata位于native memory,有独立的gc机制)。

minor gc通常采用标记-复制算法,这种算法需要先对活跃对象进行标记,然后将所有被标记对象复制到另一个完全空旷的空间,算法缺点很明显,需要额外的空旷空间,优点是空间中不会出现碎片,这种算法适合回收存有大量朝生夕灭的对象的空间,新生代就是这样的空间。

full gc通常采用标记-清除(可以通过设置决定是否压缩),这种算法首先标记活跃对象,然后回收未被标记的对象的空间,算法缺点是容易出现空间碎片,优点是不需要重置对象引用地址,这种算法适合存放大对象、老对象的空间使用,老年代就是这样的空间。

cms(与cms搭配的新生代收集器默认为ParNew,是一款多线程收集器)在进行full gc时,是并发执行的,为了避免在gc期间有对象进入老年代,cms不能在老年代满时才gc,而应当空出一些空间,但这样仍然不能保证空出的空间足够容纳对象,所以就会出现“Concurrent Mode Failure”,这时jvm就会启动Seiral Old(一种串行收集器)进行gc(如果空间仍然不足,就OOM)。

 

jvm参数设置

经过测试发现 jvm的默认设置中,Eden:Survivor1:Survivor2=8:1:1,Young Gen:Tenure Gen=1:2,新对象变成老对象需要经历15次minor gc,新创建的对象会优先存放在Eden中,如果Eden空间不足才会尝试放入Tenure中(如果Tenure也放不下,就full gc)。但这些都是可以设置的:

-Xms512M,设置初始heap大小,默认是物理内存的1/64,

-Xmx512M,设置最大heap大小,默认是物理内存的1/4,

-Xmn256M,设置新生代大小(Eden+2个Survivor),这里比较有意思的是虽然设置了-Xmn256M,但新生代真正可被利用了一定小于256M(Eden+1个Survivor),因为会有一个Survivor永远空旷,这点在gc log中就有体现,比如[ParNew: 13621K->2773K(118016K),118016K=115.25M,而我设置了-Xmn128M(115.25/128=0.9,正好符合Eden和Survivor的默认比例)。

-XX:SurvivorRatio=n,设置Survivor1:Survivor2:Eden=1:1:n,

-XX:NewRatio=n,设置Young Gen:Tenure Gen=1:n,

-MaxTenuringThreshold=15,年龄大于15的对象(经历过15次minor gc)进入老年代,

-PretenureSizeThreshold=1M,大于1M的对象直接进入老年代,

-XX:+UseConcMarkSweepGC,显示声明使用CMS进行full gc,

-XX:+UseCMSCompactAtFullCollection,在cms进行full gc后压缩空间,默认是开启的,

-XX:CMSFullGCsBeforeCompaction=n,CMS执行n次无压缩full gc后,进行一次带压缩的full gc,

-Xss2m,设置线程栈大小为1m,java中一个线程的空间大小是有限制的(线程空间并不是指Thread对象所占的空间,而是栈空间)。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现Fatal: Stack size too small异常(java.lang.StackOverflowError就和该大小相关)。

 -XX:+PrintGCDetails,打印详细gc log,

 -XX:+PrintGC或-verbose:gc,打印简单gc log,

 

 gc root

gc从gc roots开始可达性遍历,进行标记-清除: 

 

来看gc root有哪些:

  • Class - class很难被回收,因为回收条件苛刻,1加载该class的ClassLoader已经被回收,2该class的所有实例已经被回收,3该class没有被引用(其实前两条也可以归结为第三条,第一条是因为ClassLoader会将加载过的class存放在vector中,第二条是因为每个实例都会持有一个其类class对象的引用)。jvm有3个ClassLoader(Bootstrap、ext、sys),这3个ClassLoader在整个jvm运行期间都是存在的,所以由这三个loader加载的class在整个jvm运行期都不会被回收(不满足第一条),如果是自定义的ClassLoader,class(满足回收条件)就会被回收了。
  • Thread - 活着的线程(对象)
  • Stack Local - 局部变量(包括方法形参),这里最容易产生朝生夕灭的对象
  • JNI Local - jni方法的局部变量(包括方法形参)
  • JNI Global - 全局jni引用
  • Monitor Used - 被synchronized用作monitor的对象
  • Held by JVM - objects held from garbage collection by JVM for its purposes.

 

参考:https://plumbr.eu/blog/garbage-collection/minor-gc-vs-major-gc-vs-full-gc

  https://www.dynatrace.com/resources/ebooks/javabook/how-garbage-collection-works/

posted @ 2017-07-29 16:12  holoyong  阅读(237)  评论(0编辑  收藏  举报