JAVA基础:强引用、软引用、弱引用、幻象引用

问题:
强,软,弱,幻象引用是什么,他们之间的区别是什么?具体的使用场景是什么?

不同的引用其所代表的是对象的不同的可达性状态,这也影响到对象的GC。

强引用“Strong” Reference:
最常见的一种引用,平时对普通对象的引用就是强引用。比如 XX xx = new XX(); 对于一个普通的对象,如果没有其他的引用关系,那么当它超过引用的作用域,或者显示地将引用赋值为null,那么就可以被GC了。

软引用:
比强引用弱化一点,对于软引用的对象,只有当JVM认为内存不足时,才会去视图回收软引用指向的对象。所以,软引用一般用来实现内存敏感的缓存,当内存不足时,这部分缓存就会被清理掉而不会OOM。

弱引用:
比软引用更弱一点。无论内存够不够都无法避免被GC,主要用来构建一种没有特定约束的关系,在使用它时如果存在就用,不存在就重新实例化,也是很多缓存的实现方式。

幻象引用:
也就是虚引用,不能通过它访问对象。作用仅仅是提供一种确保对象被finalize以后做某些事情的机制。比如Psot-Mortem清理机制。也可以用幻象引用监控对象的创建和销毁。JAVA平台本身的Cleaner机制。

对于不同的引用,我们尝试用一张不算非常严谨的图来说明一下。
对象可达性变化图示:
JAVA基础:强引用、软引用、弱引用、幻象引用_第1张图片
图源自:极客时间JAVA 核心技术36讲。
图中各种引用导致的对象可达状态解释如下:

  • 强可达:当一个对象可以有一个或多个线程 可以不通过各种引用访问到的情况。比如我们新建一个对象,那么创建它的线程就对它就是强可达。直接访问。
  • 软可达:只要我们通过软引用才能访问到的对象
  • 弱可达:无法通过强引用或者软引用访问,只有通过弱引用访问时的状态,这种状态非常接近finalize,当弱引用被清除的时候,就符合finalize条件了。
  • 幻象可达:finalize以后的对象,并且其他引用都没有关联,只有幻象引用指向这个对象。
  • 不可达:没有引用可以到达,这个对象可以被回收了。

所有引用类型都是抽象类java.lang.ref.Reference的子类。除了幻象引用,如果对象还没有被销毁,并且不是被幻象引用指向的,都可以用Reference类中的get()方法获取原有对象。这也意味着我们可以利用软引用或弱引用,访问指定对象,并让其重新指向强引用。(人为改变了对象的可达性状态)
引用应用编程举例:
引用队列多应用

Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
counter = null;
System.gc();
try {
    // Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
    Reference<Object> ref = refQueue.remove(1000L);
    if (ref != null) {
        // do something
    }
} catch (InterruptedException e) {
    // Handle it
}

**我们可以影响软引用的垃圾收集吗?**可以的,因为软引用一般会在最后一次引用后保留几秒。
通过-XX:SoftRefLRUPolicyMSPerMB 参数设置,单位为ms。

最后,如果你想看JVM中引用和GC情况,可以使用一些工具,比如jdk8使用ParrallelGC 收集的垃圾收集日志,我们可以在启动命令中输入hotspot自带的运行参数查看:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC 。但在jvm9中已经被重构,不再存在。

JAVA9中手动达到强引用的方式:
代码如下,

class Resource {
 private static ExternalResource[] externalResourceArray = ...
 int myIndex; Resource(...) {
     myIndex = ...
     externalResourceArray[myIndex] = ...;
     ...
 }
 protected void finalize() {
     externalResourceArray[myIndex] = null;
     ...
 }
 public void action() {
 try {
     // 需要被保护的代码
     int i = myIndex;
     Resource.update(externalResourceArray[i]);
 } finally {
     // 调用 reachbilityFence,明确保障对象 strongly reachable
     Reference.reachabilityFence(this);
 }
 }
 private static void update(ExternalResource ext) {
    ext.status = ...;
 }
} 

具体例子可以参考:https://blog.csdn.net/jiahao1186/article/details/81591861 这里暂时就不赘述了。

你可能感兴趣的:(JAVA基础,java,引用,jvm,垃圾回收)