1 Star 0 Fork 165

ElonChung / Java-Review

forked from flatfish / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Java-多线程-增强篇-四种引用类型.md 7.53 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 23:09 . :fire:更新文件夹

Java - 多线程 - 增强篇 - 四种引用类型

Java中有四种引用类型,分别是如下四种

  • 强引用
  • 软引用
  • 弱引用
  • 虚引用

强引用

  • 在Java中默认声明的就是强引用,如下代码:
// 只要 o 还指向Object对象,对象 o 就不会被GC回收
Object o = new Object();
o = null;
  • 只要强引用存在,GC就永远不会回收被引用的对象,就算是内存不足的时候,JVM也会直接抛出 OutOfMemoryError,也不会去回收。
  • 如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这个时候就等待GC回收对对象即可。

软引用

  • 软引是用来描述一些非必须但是仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足的时候,系统会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常 这种特性非常适合用来做缓存
  • 在JDK1.2之后,使用 java.lang.ref.SoftReference类来表示软引用
  • 下面举例说明:先配置IDEA执行代码的VM参数 -Xms2M -Xmx3MJVM初始内存设为2M,最大可用内存为3M
/**
 * VM参数:-Xms2M -Xmx3M
 */
public class Test2 {
    public static void main(String[] args) {
        byte[] bytes = new byte[1024 * 1024 * 3];
    }
}

// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  • 现在设置为软引用,如下代码所示
/**
 * VM参数:-Xms2M -Xmx3M
 */
public class TestSoftReferenceOom {

    private static List<Object> list = new ArrayList();

    public static void main(String[] args) {
        testReferenceOom();
    }

    public static void testReferenceOom() {
        for (int i = 0; i < 10; i++) {
            byte[] b = new byte[1024 * 1024];
            SoftReference<byte[]> reference = new SoftReference<>(b);
            list.add(reference);
        }
        
        // 触发一次GC 注意:并不是一定会立刻执行GC
        System.gc();

        for (int i = 0; i < list.size(); i++) {
            Object obj = ((SoftReference) list.get(i)).get();
            System.out.print(obj + " ");
        }
    }
}

// null null null null null null null null null [B@1540e19d 
  • 此时我们发现,无论创建了多少对象,之前的对象都为null,只保留了最后一个对象,这就说明了在内存空间不足的情况下,软引用会被回收
  • 值得注意的一点 , 即使有 byte[] b引用指向对象, 且 b 是一个strong reference, 但是 SoftReference sr 指向的对象仍然被回收了,这是因为Java的编译器发现了在之后的代码中, b已经没有被使用了, 所以自动进行了优化。
  • 这时候稍稍改动一下代码如下
/**
 * VM参数:-Xms2M -Xmx3M
 */
public class TestSoftReferenceOom {

    private static List<Object> list = new ArrayList();

    public static void main(String[] args) {
        testReferenceOom();
    }

    public static void testReferenceOom() {
        byte[] b = null;
        for (int i = 0; i < 10; i++) {
            b = new byte[1024 * 1024];
            SoftReference<byte[]> reference = new SoftReference<>(b);
            list.add(reference);
        }
        
        // 触发一次GC 注意:并不是一定会立刻执行GC
        System.gc();

        for (int i = 0; i < list.size(); i++) {
            Object obj = ((SoftReference) list.get(i)).get();
            System.out.print(obj + " ");
        }
    }
}

// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  • 此时b是强引用,无法被回收。报错

弱引用

  • 弱引用的引用强度比软引用还要低一些,只有JVM在GC的之后,无论弱引用有没有被引用,都要被回收
  • 在JDK1.2之后,在java.lang.ref.WeekReference来创建弱引用
  • 代码示例
/**
 * VM参数:-Xms2M -Xmx3M
 */
public class TestSoftReferenceOom {

    private static List<Object> list = new ArrayList();

    public static void main(String[] args) {
        testWeakReferenceOom();
    }

    public static void testWeakReferenceOom() {

        for (int i = 0; i < 10; i++) {
            byte[] b = new byte[1024 * 1024];
            WeakReference<byte[]> reference = new WeakReference<>(b);
            list.add(reference);
        }
        System.gc();

        for (int i = 0; i < list.size(); i++) {
            Object obj = ((WeakReference) list.get(i)).get();
            System.out.print(obj + " ");
        }
    }
}

// null null null null null null null null null null 
  • 此时我们发现,所有的引用都为null

虚引用

  • 虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收
  • 在JDK1.2之后的 java.util.ref.PhantomReference
  • 发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
public class PhantomReference<T> extends Reference<T> {

    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
  • 那么传入它的构造方法中的 ReferenceQueue 又是如何使用的呢?

引用队列

  • 引用队列可以与软引用、弱引用以及虚引用一起配合使用,当垃圾回收器准备回收一个对象时,如果发现它还有引用,那么就会在回收对象之前,把这个引用加入到与之关联的引用队列中去。
  • 程序可以通过判断引用队列中是否已经加入了引用,来判断被引用的对象是否将要被垃圾回收,这样就可以在对象被回收之前采取一些必要的措施。
  • 与软引用、弱引用不同,虚引用必须和引用队列一起使用

拓展 - WeakHashMap

  • WeakHashMap类及其实现WeakHashMap类在java.util包内,它实现了Map接口,是HashMap的一种实现,它使用弱引用作为内部数据的存储方案。
public void test() {
    Map map;
    // 弱引用 HashMap
    map = new WeakHashMap();
    for (int i = 0; i < 10000; i++) {
        map.put("key" + i, new byte[i]);
    }
    // 强引用HashMap
    map = new HashMap();
    for (int i = 0; i < 10000; i++) {
        map.put("key" + i, new byte[i]);
    }
}
  • WeakHashMap会在系统内存紧张时使用弱引用,自动释放掉持有弱引用的内存数据。但如果WeakHashMap的key都在系统内持有强引用,那么WeakHashMap就退化为普通的HashMap,因为所有的表项都无法被自动清理。

总结

类型 回收时间 使用场景
强引用 一直存活,除非GC Roots不可达 所有程序的场景,基本对象,自定义对象等。
软引用 内存不足时会被回收 一般用在对内存非常敏感的资源上,用作缓存的场景比较多,例如:网页缓存、图片缓存
弱引用 只能存活到下一次GC前 生命周期很短的对象,例如ThreadLocal中的Key。
虚引用 随时会被回收, 创建了可能很快就会被回收 业界暂无使用场景, 可能被JVM团队内部用来跟踪JVM的垃圾回收活动
1
https://gitee.com/elonchung/Java-Review.git
git@gitee.com:elonchung/Java-Review.git
elonchung
Java-Review
Java-Review
master

搜索帮助