我所理解的java的垃圾收集


 说明:本篇文章,有很多错误.排版也不合理.请批判的阅读. (有空再整理了)

1 四种垃圾回收算法

      PS 这里介绍了4中垃圾回收算法,不代表就只有这四种。

(1)引用计数( Reference Counting )算法

     这个算法在很多面试宝典里频繁出现,当年我也被宝典所迷惑。一直到后面听同事的讲座才明白原来JAVA压根就没用这个算法作为其垃圾回收算法。但是为了本文相对的完整性,还是拿出来说一下。

    引用计数法是唯一没有使用根集的垃圾回收的法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。 

    该算法的优点是速度快,不会中断程序执行(也就是传说中的stop the world)。但是缺点也很明显:算法对程序中每一次内存分配和指针操作提出了额外的要求(增加或减少内存块的引用计数)。更重要的是,引用计数算法无法正确释放循环引用的内存块(这个问题可以在我的这篇blog里详细说明了http://chenjingbo.iteye.com/blog/1119059

(2)tracing算法(Tracing Collector)

    tracing算法是为了解决引用计数法的问题而提出,它使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象,例如对每个可达对象设置一个或多个位。

 

    在这里也顺便说下java中,可作为GC Root的对象

    1>虚拟机栈中引用的对象。虚拟机栈可以粗略的理解为java方法执行的内存模型。也就是说当前正在执行的方法中所引用的对象。

    2>方法区中的类静态属性所引用的对象

    3>方法区中的常量引用的对象

    4>本地方法栈中JNI(一般可以理解为native方法)的引用的对象

 

    标记和清除算法的缺点很明显,一是效率,因为标记和清除的效率都不高;二是碎片问题。因为清除之后会产生很多不连续的内存碎片。对于第二个碎片问题,人们想到了另外一个算法,是对标记清除算法的改善,就是下面说的标记-整理算法

(3) compacting算法(Compacting Collector)

     compacting算法也叫标记-整理算法。

    为了解决堆碎片问题,基于tracing的垃圾回收吸收了Compacting算法的思想,在清除的过程中,算法将所有的对象移到堆的一端,堆的另一端就变成了一个相邻的空闲内存区,收集器会对它移动的所有对象的所有引用进行更新,使得这些引用在新的位置能识别原来 的对象。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。

    标记-整理算法是后面说的旧生代采用的垃圾收集算法。

(4)copying算法(Coping Collector)

      复制算法是为了克服句柄的开销和解决堆碎片的垃圾回收。它将可用内存按照容量划分为相同大小的两块。每次只使用其中一块,当这一块的内存用完了,就将存活的对象复制到另外一块,然后把自己这块内存给清理掉。这样的好处很明显,每次都只对其中的一块进行内存回收,内存分配也不需要考虑碎片等问题,只要移动指针,按照顺序分配内存即可,效率很高。但是它的缺点更明显,会将内存缩小为原来的一半。

    现在所有的商业虚拟机都采用这种算法来回收新生代。

2 现在的jvm在垃圾收集上采用了何种方式

    现在的商业虚拟机的垃圾回收都采用“分代收集”方式。这个算法并没有新的思想,只是将存活周期不同的对象分为不同的几块。所以我就不将这个看做一个垃圾收集的算法。

    大家都知道,java的对象存放在java堆(Java Heap)中。而JAVA堆的描述如下:


 整个Heap可以分为两个部分:Heap = {Old + NEW = { Eden , from, to } }


 在很多资料中 Old Space也叫old generation。翻译成中文叫老年代,旧生代。而 Eden + from + to 组成了 New Space 也叫 new generation ,翻译成中文就是年轻代,新生代。至于上图中的 NewRatio,SurvivorRatio之类的参数,都是可以通过-XX:NewRatio=8 -XX:SurvivorRatio等参数配置的。这篇文章里就不多说了。主要关心的是 old Space,Eden,Fromspace,Tospace这四个名词。

还有一个名词需要解释清楚,FromSpace + ToSpace = Suvivor

 

------------------------------------------------我是分割线,下面可以讲真正的垃圾收集-------------------------------------

 

      几乎所有对象的创建都是在新生代被分配内存的(大对象直接分配在旧生代)。IBM的专门研究表明,新生代中的对象98%是朝生夕死的。所以现在的商业虚拟机都是采用复制算法来回收新生代,而且,也不需要按照1:1的比例来划分内存空间,而是将heap划分为一块较大的Eden和两块较小的Survivor(也就是上面的FromSpace和ToSpace)。当进行gc的时候,将Eden和Survivor中的某一块(FromSpace)中存活的对象复制到另一块Survivor内存(ToSpace)中。这个时候,一次MinorGC(也就是新生代GC)就算完成了,原来的FromSpace就变成ToSpace,而ToSpace变成FromSpace。在进行了几次GC(系统默认是15次,可以通过-XX:MaxTenuringThreshold来设置)后依旧存在的对象则会被移动到旧生代。当然,还有动态对象年龄判断来判定对象是否进入旧生代。

     这里需要说明的是:系统默认的Eden与Survivor的比例是8:1,也就是每次新生代中可用的内存空间为整个新生代内存的90%,只有10%的内存是被浪费的。这样比纯粹的复制算法(50%)好很多。

     年老代的GC又叫Full GC或者Major GC。一般情况下,发生FullGC的时候会伴随一次MinorGC(也就是年轻代GC)。上面已经说了Full GC的算法采用的是标记-整理算法(据说还有标记-清理算法)。而且从效率上来说,一般情况下会比Minor GC慢10倍以上。

 

 

    最后说一下,上面说的都是垃圾收集算法,换句话说上面说的都是方法论,具体的实现就是对应具体的垃圾收集器。JAVA的虚拟机规范中并没有规定应该如何实现垃圾收集器,所以不同的厂商,不同版本的虚拟机所提供的垃圾收集器都有可能有很大的不同。最后放一个图说明下Sun 的 HotSpot虚拟机1.6 Update22所包含的收集器。这幅图是sun的某人在2008年2月画的,其中的问号现在已经基本可以确定是G1收集器。因为JDK1.6 Update14就引入了G1的 Early Access版。据说JDK7推出以后,会有一个商业版的G1收集器出来,大家期待吧

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值