JAVA-DIY-11-WEEK

Persistence is not necessarily successful, but not sure will not succeed.

Posted by Hyuga on June 5, 2019

第11次讨论主题:对象引用类型

引用类型

JAVA提供了四种引用类型,分别是: 强引用软引用弱引用虚引用

用自己接触的业务场景或自己的理解角度说明软引用和弱引用的价值(若有代码可附上)

Java一大特点:GC-垃圾回收机制!不需要开发者自行管理内存。

作用:

  • 允许开发者通过代码方式自定义某些对象的生命周期
  • 便于JVM进行垃圾回收

为什么会提供四种引用类型?

试想在有限的内存空间内,对象越创建越多,而gc回收也不是什么对象都能回收的,比如正在使用的对象会继续占着内存。最终导致:OutOfMemory.

如果gc每次把无关联引用回收的同时,顺道也回收掉一些关联不是很强的对象引用,对于内存管理来说是否更合理,更高效,也更安全,有效避免了OOM.

各种引用类型介绍:

  • 强引用(StrongReference)
    • 指JVM内存管理器从根引用集合(ROOt Set)出发遍寻堆中所有可到达的对象引用关系,也是常用的引用类型,如Object obj = new Object();
    • 只要强引用存在,即便内存不足,JVM会抛出OOM错误也不会回收该对象。
  • 软引用(SoftReference)
    • 指一些有用但不是必须的对象。
    • 在Java中用java.lang.ref.SoftReference类来表示。
    • 只有在内存不足的时候,JVM才会回收软引用对象。
  • 弱引用(WeakReference)
    • 指一些非必需的对象,与软引用不同的是当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
    • 在java中,用java.lang.ref.WeakReference类来表示。
  • 虚引用(PhantomReference)
    • 虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。
    • 在java中用java.lang.ref.PhantomReference类表示。
    • 当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

很遗憾的是工作中除了到处可见的强引用代码,确实没用过软引用和弱引用,对java.lang.ref包也是知之甚少。因此以下也就从个人理解方面提出一些价值猜想:

用作缓存,利用垃圾回收,内存不足时,JVM才会回收软引用对象的特性,可以把一些强引用对象封装成软引用对象进行操作,正常情况下可以从软引用中读取强引用对象,内存不足时清空软引用对象,同时也清空强引用对象。有效避免OOM问题。

弱引用每次gc都会回收,具体场景实在没什么头绪。应该是用在那种不需要频繁调用的,而且用完后可以及时销毁对象的场景。

CODE

软引用

public class SoftRefTest {

    private static Map<String, SoftReference<Hyuga>> cacheMap = new HashMap<>();

    private static void addToCache(String key) {
        // 强引用对象
        Hyuga hyuga = new Hyuga(1, "hyuga");
        // 软引用对象
        SoftReference<Hyuga> hyugaSoft = new SoftReference<>(hyuga);
        // 添加该对象到Map中使其缓存
        cacheMap.put(key, hyugaSoft);

    }

    private static Hyuga getByPath(String key) {
        // 从缓存中取软引用对象
        SoftReference<Hyuga> hyugaSoft = cacheMap.get(key);

        // 判断是否存在软引用
        if (hyugaSoft == null) {
            return null;
        }
        // 取出对象,如果由于内存不足hyuga被回收,将取得空
        return hyugaSoft.get();
    }

    public static void main(String[] args) throws InterruptedException {
        addToCache("hyuga");
        for (int i = 0; i < 1000; i++) {
            System.out.println(i + " ==========> " + getByPath("hyuga"));
            TimeUnit.SECONDS.sleep(1);
        }
    }

}

@Getter
@Setter
@AllArgsConstructor
@ToString
class Hyuga {
    private int age;
    private String name;
}

当内存不足时,getByPath("hyuga")的值为null

弱引用

public class SoftRefTest {

    private static Map<String, WeakReference<Hyuga>> cacheMap = new HashMap<>();

    private static void addToCache(String key) {
        // 强引用对象
        Hyuga hyuga = new Hyuga(1, "hyuga");
        // 软引用对象
        WeakReference<Hyuga> hyugaSoft = new WeakReference<>(hyuga);
        // 添加该对象到Map中使其缓存
        cacheMap.put(key, hyugaSoft);

    }

    private static Hyuga getByPath(String key) {
        // 从缓存中取软引用对象
        WeakReference<Hyuga> hyugaSoft = cacheMap.get(key);

        // 判断是否存在软引用
        if (hyugaSoft == null) {
            return null;
        }
        // 取出对象,如果由于内存不足hyuga被回收,将取得空
        return hyugaSoft.get();
    }

    public static void main(String[] args) throws InterruptedException {
        addToCache("hyuga");
        for (int i = 0; i < 1000; i++) {
            System.out.println(i + " ==========> " + getByPath("hyuga"));
            TimeUnit.SECONDS.sleep(1);
        }
    }

}

@Getter
@Setter
@AllArgsConstructor
@ToString
class Hyuga {
    private int age;
    private String name;
}

只要gc执行过一次,无论内存够不够,弱引用对象都会被回收,所以getByPath("hyuga")的值为null