JVM垃圾回收机制

------------------------------------------------一、如何判断一个对象已死?即需要回收------------------------------------------------

1、引用计数算法:
------每个对象都有一个引用计数器,当有引用链接至对象时,引用计数+1,当引用离开作用域或被置null时,引用计数-1。 当发现计数值变为0,该对象不在使用。

------主流java虚拟机不在使用该算法,最主要的原因是无法解决“对象之间相互引用”的问题:
lass A{ 
    public B b; 


class B{ 
    public A a; 


public class Main{ 
    public static void main(String[] args){ 
             A a = new A(); 
             B b = new B(); 
         a.b=b; 
            b.a=a; 
        } 


在函数的结尾,a和b的计数均为2
先撤销a,然后a的计数为1,在等待b.a对a的引用的撤销,也就是在等待b的撤销
对于b来讲,也是同理
两个对象都在等待对方撤销,所有这两个资源均不能释放




2、可达性分析算法
------我们通过一系列称之为“GC Roots”的对象作为起始点(如虚拟机栈中引用的对象,即栈帧中的本地变量表;方法区中类静态属性、常量引用的对象;本地方法栈中JNI应用的对象),从这些节点向下搜索,就可以找到所有的可到达对象,搜索过的路径称之为“引用链”。一个可到达对象,一定被根引用,或者被其他可到达对象引用;相反则对象不可达,即不可用。





3、4中引用(见外面文档)


4、回收方法区(方法区存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用永久代(PermanetGeneration)来存放方法区)
永久代的垃圾回收分两部分:
------废弃常量:回收废弃常量和java堆中的对象回收类似,检查该常量是否有用到。如常量池中的字符串
------无用的类:满足3个条件才能算无用,才可能被回收,不是一定: 类的所有实例被回收;类的ClassLoader被回收;Class对象没有被引用,无法反射访问;











------------------------------------------------二、回收算法------------------------------------------------

1、标记清除:
1)标记:标记出所有要回收的对象,如上面“可达性算法”:遍历所有的引用,进而找到所有存活的对象,每找个存活的对象,就将其标记
2)清除:统一回收被标记的对象
缺点:效率不高;清除后有大量不连续的内存片段



2、复制算法:
为了提高效率,将内存分为大小一样的两块,每次使用一块;当一块使用完了后,将存货对象复制到另一块内存;清除已使用过的一块内存清空
缺点:复制算法在存活率较高时进行较多的复制操作,效率会变低,且浪费了50%的空间。老年代不能使用该方式


3、标记整理
和标记清除类似,但是标记后不直接清空回收内存,而是将存货对象向一端移动,然后清理掉边界以外的内存



4、分代收集算法
当前商业虚拟机的垃圾收集都采用“分代收集”:将java堆分为新生代和老年代,新生代每次收集时都有大量的对象死去,只有少量存活,采用“复制算法”;老年代对象存活率较高,使用“标记清除”或者“标记整理”算法回收


以JDK1.6版本的 JVM为例说明Java GC、新生代、老年代:
1)默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2,即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。

2)其中,新生代 ( Young )被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。默认的:Edem : from : to = 8 :1 : 1 。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块Survivor区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
                                                                               
3)Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC、FullGC ( 或称为 Major GC )。
    Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。
    新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。 当一个对象被判定为 "死亡" 的时候,GC 就有责任来回收掉这部分对象的内存空间。新生代是 GC 收集垃圾的
频繁区域。
    当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,
即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即
from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定
 ),这些对象就会成为老年代。但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。


4)Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。
Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象
分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。











------------------------------------------------------------------------------------------------



finalize:
    工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并在下一次垃圾回收动作发生时才会真正回收对象占用的内存,它能在垃圾回收时做一些重要的清理工作。
    无论对象如何创建的,垃圾回收器都会负责释放对象占用的内存,垃圾回收器只负责回收那些通过new分配的内存。这就将finalize()的需求限制到了一种特殊情况,即通过某种创建对象以外(并非使用new)的方式为对象分配了存储空间,如分配内存时采用类似C语言中malloc()分配存储空间的做法,使用了本地方法(java中调用非java方法),需要在finalize()方法中调用本地方法使用free()释放内存。
    不能保证垃圾回收器会删除某个对象,因为在java虚拟机并未面临内存耗尽的情况下,它是不会浪费时间去执行垃圾回收的,因此放在finalize()中的代码无法保证运行,因此建议不要重写finalize();但是可以验证某些对象是否被回收。














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值