美文网首页android知识储备
Java引用及GC垃圾回收机制

Java引用及GC垃圾回收机制

作者: 小的橘子 | 来源:发表于2019-03-04 13:55 被阅读23次

    Java引用

    1. StrongReference 强引用
    2. SoftReference 软引用
    3. WeakReference 弱引用
    4. PhantomReference 虚引用

    1. StrongReference

    介绍

    我们平常用的最多的就是强引用了

    Object obj = new Object();
    

    这种形式的引用称为强引用

    特点

    • 强引用所指向的对象在任何时候都不会被系统回收;
    • 由于特点1的原因,强引用可能导致内存泄漏。
      强引用只有引用置为null,垃圾回收器才会去回收该资源,否则不会去回收,即使在内存不足的情况下系统宁愿报oom(OutOfMemory)错误,也不会去回收强引用资源.

    2. SoftReference

    当JVM中的内存不足的时候,垃圾回收器会释放那些只被软引用所指向的对象。如果全部释放完这些对象之后,内存还不足,才会抛出OutOfMemory错误。软引用非常适合于创建缓存。

    package com.nan.testproject;
    
    import java.lang.ref.ReferenceQueue;
    import java.lang.ref.SoftReference;
    
    /**
     * -Xms256m
     * -Xmx2g
     */
    public class ReferenceTest {
        public static void main(String[] args) {
    
            ReferenceQueue referenceQueue = new ReferenceQueue();
            // 软引用对象中指向了一个占用空间为1g的数据
            SoftReference<int[]> softReference =
                    new SoftReference<>(new int[1024 * 1024 * 1024 / 4], referenceQueue);
    
            // 主动调用一次gc,由于jvm最大堆内存-Xmx设置为2g,此时JVM的内存够用,故softReference引用的对象未被回收
            System.gc();
            System.out.println(softReference.get());
    
            // 申请占用1g大小的数组,此时JVM内存不足,会导致一次自动的gc,会回收softReference对象中指向的数组对象
            int[] datas = new int[1024 * 1024 * 1024 / 4];
            SoftReference ref = null;
            // 当softReference中数组被回收后,会将引用放入绑定的referenceQueue中,referenceQueue.poll()也就不为null了
            while ((ref = (SoftReference) referenceQueue.poll()) != null) {
                // softReference中的int数组对象已被回收,此时可以置softReference该强引用为null
                // softReference = null;
            }
            System.out.println(softReference.get());
        }
    }
    
    

    输出

    [I@75b84c92
    null
    

    注意,上面代码创建创建软引用部分,如果代码改为下面部分

    int[] data = new int[1024 * 1024 * 1024 / 4];
    SoftReference<int[]> softReference =
            new SoftReference<>(data, referenceQueue);
    

    运行会报OOM,如下

    [I@75b84c92
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at com.nan.testproject.ReferenceTest.main(ReferenceTest.java:26)
    

    JVM进行软引用垃圾回收时只会回收被弱引用关联的对象,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象。(弱引用和虚引用也是如此)

    3. WeakReference

    弱引用相比软引用拥有更短的生命周期,垃圾收集器一旦发现了只具有弱引用的对象,不管当前内存是否足够,都会回收它的内存。

    /**
     * -Xms256m
     * -Xmx2g
     */
    public class ReferenceTest {
        public static void main(String[] args) {
            WeakReference<byte[]> weakReference = new WeakReference<>(new byte[1024*1024]);
            System.out.println(weakReference.get());
            // 通知JVM回收资源
            System.gc();
            System.out.println(weakReference.get());
        }
    }
    

    JVM配置最大堆内存为2g,而代码中字节数组只占用1m内存,当JVM进行垃圾回收后,其就被回收了。

    应用

    1. ApplicationPackageManager中图标缓存用到
    2. WeakHashMap

    4. Phantom Reference

    虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

    要注意的是,虚引用必须和ReferenceQueue关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

    /**
     * -Xms256m
     * -Xmx2g
     */
    public class ReferenceTest {
        public static void main(String[] args) {
            ReferenceQueue referenceQueue = new ReferenceQueue();
            PhantomReference<byte[]> phantomReference = new PhantomReference<>(new byte[1024*1024],referenceQueue);
            System.out.println(phantomReference.get());
        }
    }
    

    运行结果输出null

    ReferenceQueue

    当gc(垃圾回收线程)准备回收一个对象时,如果发现它还仅有软引用(或弱引用,或虚引用)指向它,就会在回收该对象之前,把这个软引用(或弱引用,或虚引用)加入到与之关联的引用队列(ReferenceQueue)中。如果一个软引用(或弱引用,或虚引用)对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。

    当软引用(或弱引用,或虚引用)对象所指向的对象被回收了,那么这个引用对象本身就没有价值了,如果程序中存在大量的这类对象(注意,我们创建的软引用、弱引用、虚引用对象本身是个强引用,不会自动被gc回收),就会浪费内存。因此我们这就可以手动回收位于引用队列中的引用对象本身。

    Java GC机制

    1. 什么时候

    GC具有不确定性,具体由JVM的回收机制决定,但内部不足时肯定会被调用,或者应用比较空闲时。我们无法手动指定其进行回收,也可以调用System.gc来提醒垃圾收集器进行收集,大多数会执行,但不保证会执行。主流Java虚拟机的垃圾收集器都采用分带收集算法(Generation Collection)。

    1. Minor Collection:新生代垃圾收集
    2. Full Collection:对老年代进行垃圾收集,收集频率较低,耗时长,速度一般会比Minor GC慢10倍以上。

    Minor GC触发条件:

    当Eden区满时,触发Minor GC。

    Full GC触发条件:

    1. System.gc()方法的调用

    此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,可通过通过-XX:+ DisableExplicitGC来禁止RMI(Java远程方法调用)调用System.gc。

    1. 老年代空间不足

    升到老年代的对象大于老年代剩余空间,会执行Full GC后空间仍然不足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

    当然可以通过调优,用-XX:NewRatio控制新生代和老年代的占用空间比列,或者用-XX:MaxTenuringThreshold控制对象经历多少次Minor GC才晋升到老年代,默认为15。使得对象存储空间延迟达到Full GC,从而使得计时器引发gc时间延迟OOM的时间延迟,以延长对象生存期

    2. 对什么东西

    判断对象是否需要回收有两种垃圾标记算法:1. 引用计数算法,2. 根搜索算法

    当对象引用计数为0,或者超出了作用域(但不包含超出作用域还被特殊强引用GC Roots持有),对象不可达时会进行标记,当进行GC时,会判断该对象是否重写了finilize方法,如果重写则回调用,但如果调用后还是不可达,那该对象在下次gc时会被回收。

    3. 做了什么事情

    删除不使用的对象,回收内存空间。对于Minor GC会采用复制清理算法(存活对象复制到To Survivor,回收Eden和from Survivor需要回收的对象,然后将To survivor和From Survivor空间位置互换)。对于Full GC采用的是标记-压缩算法(将回收对象清除后,把存活对象向左移动,使占用内存工整)
    java GC是在什么时候,对什么东西,做了什么事情?

    相关文章

      网友评论

        本文标题:Java引用及GC垃圾回收机制

        本文链接:https://www.haomeiwen.com/subject/dsxiuqtx.html