Java 垃圾回收机制
分代回收的思想
在Java中,我们必须了解不同对象的生命周期是不同的的事实。为了更好的回收垃圾,基本上所有的jvm都采用了按代的垃圾回收机制。
分代回收的最基本做法,就是将内存中的区域划分为3个不同的部分:年轻代,年老代,永久代(持久代)。
-
年轻代 :一个Eden区,两个Survivor区,三个区默认占用年轻代内存比例(8:1:1),存放在该区的对象生命周期很短。
-
Eden(伊甸区):绝大多数创建的对象,都存放在该区
-
S0(From):Eden区经过一轮”Young GC”(“Minor GC”)后,还存活的对象存放到S0区,并清空Eden区
-
S1(To):当S1区堆积满对象时,将还存活的对象移动到S1区,并清空S0区,这时候,S1区就变成S0了。
-
-
年老代:年轻代中,经过几轮Young GC后,将还存活的对象移动到年老代。一般情况下,年老代的控件比年轻代大,存放的对象更多,当年老代内存不足的时候,会执行Full GC。如果对象很大(比如长字符串,大数组等),年轻代空间不足,对象会直接存放到年老代中。
-
永久代:存放JVM需要的应用元数据(在应用里使用的类和方法等),常量等。
注意,在jdk1.6后,永久代被取消了。
什么时候需要执行GC
Young GC(只针对年轻代的)
- 当新对象生成,并且在Eden区申请空间失败时
- 把尚且存活的对象移动到Servivor区,然后整理Survivor的两个区时
- 每次执行Full GC,都会触发Young GC
Full GC(针对整个堆)
- System.gc(),Runtime.getRuntime().gc(),heap dump
- 向年老代/永久代申请空间,但空间不足时
回收哪些内存
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的
什么是GCRoots?
- 虚拟机栈(栈帧中的本地变量表)中所引用的对象
- 方法区中的类静态属性引用的对象
- 方法区中的常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
被GC判定为不可达对象一定会被回收么?
jvm中,一个对象真正的被GC至少要经历两次的标识,第一次是经过GCRoots来标识,如果标识上对象不可达了,那么这个对象会进行下一轮的刷选(如果该对象重写finalize方法,则会认为有必要进行下一轮的判定,否则直接回收)。 如果重写finalize()方法,jvm就会将该对象加入的F-Queue中,稍后,jvm会创建一条自动建立的、低优先级的Finalizer线程去执行GC判定(注意这里是需要时间的),如果执行完后,该对象还没重生的话,那么对象就会被真正的回收
如何执行GC
- 年轻代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
- 年老代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者”标记—整理”算法来进行回收。