java jvm 垃圾收集算法

来源:互联网 发布:北津学院教务网络系统 编辑:程序博客网 时间:2024/05/16 12:36

引用计数算法

由于之前在看《深入理解java虚拟机》之前先看了《深入理解android内核设计思想》,两本书上都有写到这个算法。关于如何实现java的垃圾收集机制,就是在object类上加上一个引用计数器,我们知道java中所有类都继承自object,当这个对象被人引用一次那么引用计数器就加1,引用结束了就减1,垃圾收集的时候就简单了,当这个对象没有人用了,也就是计数器为0了,那么就把它回收掉。但是主流的jvm里面并没有选用这个算法。因为它很难解决对象 之间循环引用 的问题。不过在安卓虚拟机里面有一个智能指针的概念倒是跟这个差不多,通过一个巧妙的办法解决了相互循环引用 的问题。
先看一个这个问题到底是怎么产生的。
比如说
<pre name="code" class="java">package test;public class Demo {private Object count = 0;public static void main(String[] Args) {new Demo().test();}private void test() {Demo d1 = new Demo();Demo d2 = new Demo();d1.count = d2;d2.count = d1;d1 = null;d2 = null;System.gc();}}

上述就是一个典型的相互引用的例子,先说一个在安卓上的智能指针是如何解决的,它通过引入弱引用解决了这个问题,规定强引用计数为0时,不论弱引用是否为0都可以delete自己,在安卓中这个规则是可以调整的,这样虽然成功的解决了“死锁问题”,但是会有产生野指针的可能 ,比如d1强指针数为0了该 回收了,可是d2还持有它的弱引用,这里用这个指针来访问d1会产生严重的问题。安卓上因此有一个特别规定,弱指针先升级为强指针 ,才能访问它所指向的目标对象 。可能会有人问,java不是没有指针么。。java是没有。是因为jvm已经把指针的问题解决了,我们这里讲的是jvm和android系统。
jvm没有采用引用计数算法那么它采用的是什么呢。是下面的这个算法。

可达性分析算法

这个算法的基本思想是通过 叫做GC Roots的对象作为起始点,从这些节点 向下搜索,搜索走过的路径叫做引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可引用的。在java C#包括古老的Lisp语言中都是用这个方法判断对象是否存活的。

java中可做为GC Roots的对象 :

虚拟机栈中引用的对象

方法 区中类静态发生引用的对象 

方法区中常量 引用 的对象 

本地方法栈中JNI引用的对象 

java中的各种引用

由强到弱依次分为:

强引用

软引用

弱引用

虚引用

曾经还用到过,因为开发一个安卓app,里面有一个功能是播放 gif图,图的解码器是用的一个开源的项目 ,但这个项目存在 一个问题就是经常会oom(outofmemory),自己还改良一下,就是通过引用软引用和弱引用,希望能加大垃圾收集器的回收力度。结果效果并不好。。那么接着说这四个引用的强度区别 

强引用就是我们通常用的new

软引用就是在要发生oom之前进行回收

弱引用就算内存够用也会被回收

虚引用唯一的作用就是在回收的时候收到一个系统 通知 。

这里我写了一个测试类

package test;import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;import java.lang.ref.SoftReference;import java.lang.ref.WeakReference;public class Demo {private String name;public Demo(String name) {super();this.name = name;}public Demo() {super();}public static void main(String[] Args) {new Demo().test();System.gc();}@Overrideprotected void finalize() throws Throwable {// TODO Auto-generated method stubsuper.finalize();System.out.println("我死啦我死啦" + this.name);}void test() {Demo a = new Demo("soft");SoftReference<Demo> d1 = new SoftReference<Demo>(a);Demo b = new Demo("weak");WeakReference<Demo> d2 = new WeakReference<Demo>(b);Demo c = new Demo("pham");ReferenceQueue<Demo> temp = new ReferenceQueue<Demo>();PhantomReference<Demo> d3 = new PhantomReference<Demo>(c, temp);}}
运行结果几次各有不同:

结果 1:

我死啦我死啦pham
我死啦我死啦weak
我死啦我死啦soft
我死啦我死啦null

结果2:

我死啦我死啦pham

结果3:

我死啦我死啦pham
我死啦我死啦weak

这里可以看出虚引用的确是一点活路都没有,而且垃圾回收也的确是不确定 的。不过总的强度没有错误 ,是从弱到强进行回收的


垃圾收集算法

标记-清除算法

算法如其名,就是先标记出哪个是需要回收的,然后再进行回收,不过有一个严重的问题,现在我们想象内存是一个棋盘,然后被标记的是几个块,现在就是可用的内存和要回收和内存块是相间的,跟熊猫是的。回收过后就会产生好多好内存碎片,以至于以后要分配较大的内存的时候 找不到地方。

复制算法

它将可用内存分成的两大块,每次只使用其中一块,当这一块用完了,就将存活的对象复制到另一块上去,然后那块就完全被 清理干净 ,典型的用空间换时间的算法,这虽然效率高,但是未免也太费内存了。

标记-整理算法

跟标记清除算法差不多,只不过为了解决它的碎片问题又加 上了一个碎片整理功能 。

分代收集算法

这是当前虚拟机在采用的算法,就是根据各个年代(新生代和老年代)采用不同的算法进行回收,新生代每次都 有大量对象死去,那么就采用复制算法,只需要付出少量对象的复制 成本 ,而老年代中存活率高,所以采用整理或清除算法。

我发现其实好的算法并不是单一职能,都 是几个算法结合起来使用,这是一个例子,又比如linux中的文件系统存储分配算法,也是采用链表索引加内存块分配 算法结合的办法。




0 0
原创粉丝点击