您的位置:首页 > 其它

垃圾收集器与内存分配策略

2016-12-30 18:34 183 查看
1.对象已死的判断

1.1引用计数算法

给对象添加一个计数器,每有一个地方引用它时,计数器加1,当引用失效时,计数器减1,计数器为0的对象是已死对象。java没有选择这种计数算法,因为存在对象相互循环引用问题。看下面例子:

// 对象相互引用情景
class A{
A instance ;
}
public static void main(String[] args){
A a1 = new A();
A a2 = new A();
a1.instance = a2 ;
a2.instance = a1 ;
}


1.2根搜索算法

通过一系列的名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(从GC Root到这个对象不可达)时,证明这个对象是不可用的。看下图:



2.对象回收过程

在根搜索算法中不可达的对象也并非是”非死不可的”,这时它们处于缓刑阶段,至少要经历两次标记过程:如果对象没有与GC Roots相链接的引用链,将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象覆盖finalize()或者finalize没被虚拟机调用过,则这个对象会被放置在一个F-Queue队列中,稍后由虚拟机建立的Finalizer线程扫描队列执行对象的finalize()方法。所以finalize()不能阻塞。对象可以在finalize方法中重建引用链,就能拯救自己,如果没有拯救,GC会对F-Queue中的对象进行第二次小规模标记。

3.垃圾收集算法

3.1标记-清除算法

首先标记出所有需要回收的对象(根搜索算法判断已死),在标记完成后统一回收所有被标记的对象。缺点:标记和清除效率低;标记清除后会产生大量不连续的内存碎片,碎片太多会导致分配大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

3.2复制算法

将可用的内存按容量均分两块,每次只能使用其中一块,当这块用完了,就将还存活的对象复制到另一块,然后把已经使用的这块一次性全部清理。缺点:内存缩为原来一半。

3.3标记-整理算法

标记过程与标记清除算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都想一端移动,然后直接清理掉端边界以外的内存。

3.4分代收集算法

一般我们把java堆分为新生代和老年代,分代收集算法就是根据新生代和老年代特定各自选择适当的收集算法。

在新生代中:每次垃圾收集都会有大批对象死去,少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

在老年代中:对象存活率高,没有额外空间对它进行分配担保,就采用标记-整理算法进行回收。

4.堆空间详解

堆空间的结构图:



s0+s1=Survivor区

HotSpot JVM把新生代分为了三部分:1个Eden区和2个Survivor区(分别叫from(s0)和to(s1))。默认比例为8:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。



新生代GC和老年代GC



大对象直接进入老年代

虚拟机提供参数 -XX:PretenureSizeThreshold ,来设置阈值,大于这个值得对象直接在老年代中分配。

老生常谈:深圳有爱好音乐的会打鼓(吉他,键盘,贝斯等)的程序员和其它职业可以一起交流加入我们乐队一起嗨。我的QQ:657455400
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: