Java新生代老年代的划分及垃圾回收算法 复习总结
个人有道云笔记地址:Java新生代老年代的划分及回收算法复盘...
链接:http://note.youdao.com/noteshare?id=d73849e7e360d31540f624cd91f7b7e5
Java新生代老年代的划分及回收算法复习总结
Java堆是垃圾回收器管理的主要区域,百分之九十九的垃圾回收发生在Java堆,另外百分之一发生在方法区,因此又称之为”GC堆”。根据JVM规范规定的内容,Java堆可以处于物理上不连续的内存空间中。
JVM的内存分配回收算法:复制算法 标记算法 标记整理算法
如何判断对象存活可用?
可达性分析: 通过一系列称为 GCRoot 对象做起点,从这些节点向下搜索,搜索所走过的路径称为引用链,如果一个对象在引用链上,就说是可达的,这种对象就是需要存活下来的!!!
作为GC Roots的对象包括下面几种:
1.当前虚拟机栈中局部变量表中的引用的对象
2.当前本地方法栈中局部变量表中的引用的对象
3.方法区中类静态属性引用的对象
4.方法区中的常量引用的对象
当对象在 Eden ( 包括一个 Survivor 区域(就是指两个Survivor 区其中一个),这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 前,会标记Eden和From 中存活的对象 不需要清除的对象,然后使用复制算法将标记的对象复制到另外一块 Survivor(即Survivor区域中的to区域) 区 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些存活的对象的年龄设置为 1,(然后再次同上,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,CMS默认6岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代
补充:
但这也不是绝对的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。
还有 当代码中调用 System.gc() 对于可达的对象会提前进入老年代,并且 这个方法先minor gc 然后在 full gc。
System.gc(); //告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的
讲一下gc 的三个算法:
1.标记清除算法(最基础):分为标记和清除两个部分:首先标记出所有需要回收的对象,这一过程在可达性分析过程中进行。在标记完之后统一回收所有被标记的对象。
图解·:
缺点:
- 效率:标记和清除这两个过程的效率不高
- 空间:清除之后会产生大量不连续的内存碎片,内存碎片太多会导致以后的程序运行中无法分配出较大的内存,从内不得不触发另外的垃圾回收
2.复制算法:(针对Java堆中的新生代内存垃圾回收)
复制算法将堆中可用的新生代内存按容量划分成大小相等的两块内存区域,每次只使用其中的一块区域。当其中一块内存区域需要进行垃圾回收时,会将此区域内还存活着的对象复制到另一块上面,然后再把此内存区域一次性清理掉。
优点:每次都是对整个新生代一半的内存区域进行内存回收,内存分配时也就不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配即可。此算法实现简单,运行高效
现在主流的虚拟机,包括HotSpot都是采用的这种回收策略进行新生代内存的回收。
HotSpot实现的复制算法流程如下:
1. 当Eden区满的时候,会触发第一次Minor gc,把还活着的对象拷贝到Survivor From区;当Eden区再次触发Minor gc的时候,会扫描Eden区和From区域,对两个区域进行垃圾回收,经过这次回收后还存活的对象,则直接复制到To区域,并将Eden和From区域清空。
2. 当后续Eden又发生Minor gc的时候,会对Eden和To区域进行垃圾回收,存活的对象复制到From区域,并将Eden和To区域清空。
3. 部分对象会在From和To区域中复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代。
3.标记整理算法:(针对老年代内存垃圾回收) 复制算法在对象存活率较高的老年代会进行很多次的复制操作,效率很低,所以在栈的老年代不适用复制算法。
标记-整理算法”。标记过程仍与”标记-清除”过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。