android知识储备

Java引用及GC垃圾回收机制

2019-03-04  本文已影响23人  小的橘子

Java引用

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

1. StrongReference

介绍

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

Object obj = new Object();

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

特点

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是在什么时候,对什么东西,做了什么事情?

上一篇下一篇

猜你喜欢

热点阅读