前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文带你读懂Java的强引用、软引用、弱引用、虚引用

一文带你读懂Java的强引用、软引用、弱引用、虚引用

作者头像
后台技术汇
发布2022-05-28 12:07:01
4310
发布2022-05-28 12:07:01
举报
文章被收录于专栏:后台技术汇

开讲前,我们先回顾下JVM的基本结构。根据《Java虚拟机规范(Java SE 7版)》。Java虚拟机所管理的内存将会包括以下几个运行时数据区域:

  • 程序计数器(Program Counter Register):当前线程执行的字节码指示器
  • Java虚拟机栈(Java Virtual Machine Stacks):Java方法执行的内存模型,每个方法会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 本地方法栈(Native Method Stack):(虚拟机使用到的)本地方法执行的内存模型。
  • Java堆(Java Heap):虚拟机启动时创建的内存区域,唯一目的是存放对象实例,处于逻辑连续但物理不连续内存空间中。
  • 方法区(Method Area):存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 运行时常量池(Runtime Constant Pool):方法区的一部分,存放编译器生成的各种字面值和符号引用。

图1 java 虚拟机运行时数据区

本文即将介绍到的:Java的强引用、软引用、弱引用、虚引用,都与JVM的GC有着莫大的关系。

我们都知道,常用的GC回收关注的JVM区域为是Java堆(包含了方法区{外界也称为“常量池”}),而我们的引用(无论是强/软/弱/虚)都是指向于堆的某一块内存区域。Java堆与方法区,只有在程序运行期间才知道会创建哪些对象,这部分内存的分配和回收都是动态的。

主流商用程序语言的主流实现中,都是通过可达性分析(Reachability Analysis)来判定对象是否存活。

可达性算法基本思路是:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜素走过的路径称为“Reference Chain”(引用链),当下一个对象到GC Roots没有任何引用链相连时,则证明该对象不可用

图2 可达性分析算法判断对象是否存活

进入今天的主题:再谈引用是什么。无论是何种GC回收算法,判断对象的存活都与“引用”有关。在SDK1.2之前,Java对引用定义很传统:如果reference类型的数据存储的数值代表的是另外一块内存的起始地址,就称为“这块内存代表着一个引用”。

这种定义比较虽纯粹但狭隘,对于如何描述一类“食之无味,弃之可惜”的对象显得无能为力。我们后来希望描述一类对象:当内存空间足够时,能够保留在内存之中;如果内存空间在GC回收后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

于是,在JDK1.2之后,Java对引用概念进行了扩充,把引用分类为:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,它们的引用强度一次逐渐减弱。

强引用(Strong Reference)

在程序代码中普遍存在的,类似下面这类的引用,只要强引用存在,那么GC Collector就永远不会回收掉被引用的对象。

代码语言:javascript
复制
Object obj = new Object();
  • 例子
代码语言:javascript
复制
/**
 * <p>
 *     强引用:当对象指向内存为空不关联时,才会被GC回收,否则永远不会回收。
 * </p>
 */

public class ObjectReference {
  public static void main(String[] args) {
    String str = "StrongReference";
    System.out.println(str);
    System.gc();
    System.out.println(str);
    str = null;
    System.out.println(str);
  }
}
  • 输出
代码语言:javascript
复制
StrongReference
StrongReference
null

软引用(Soft Reference)

描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统即将要发生内存溢出异常之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。JDK1.2之后,提供了 SoftReference类实现软引用。

  • 例子(加入虚拟机启动参数:-Xms10m -Xmx40m,表示JVM最大堆内存为12M)
代码语言:javascript
复制


/**
 * <p>
 *     软引用:当内存不足时,才会触发JVM进行GC回收。
 *       适合做缓存。
 * </p>
 */
public class SoftReferenceObject1 {

  public static void main(String[] args) {
    softRefenceTest();
  }
  /**
   *  仅仅gc, 还不会回收。需内存不够 GC的时候才回收软引用。
   * 启动参数:
   * -Xmx16m  -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:NewSize=2m -XX:MaxNewSize=2m
   */
  private static void softRefenceTest() {
    final int _8M = 8 * 1024 * 1024;
    List<SoftReference> list = new ArrayList<SoftReference>();
    System.out.println("add 8m  -1");
    list.add(new SoftReference(new byte[_8M]));
    System.out.println("add 8m  -2");
    list.add(new SoftReference(new byte[_8M]));
    System.out.println("add 8m  -3");
    list.add(new SoftReference(new byte[_8M]));
    System.out.println("add 8m  -4");
    list.add(new SoftReference(new byte[_8M]));
    System.out.println("add 8m  -5");
    list.add(new SoftReference(new byte[_8M]));
    System.out.println("add 8m  -6");
    list.add(new SoftReference(new byte[_8M]));
    System.gc();
    list.stream().forEach(r-> System.out.println(r.get()));
   }
}
  • 输出
代码语言:javascript
复制
add 8m  -1
add 8m  -2
add 8m  -3
add 8m  -4
add 8m  -5
add 8m  -6
null
null
null
[B@5f4da5c3
[B@443b7951
[B@14514713
  • 解析
代码语言:javascript
复制
启动程序时,设置堆内存最大为40M;(-Xms10m -Xmx40m)
而后程序依次申请分配8M的数组连续存储空间,
经过6次创建对象操作,在5次时,已经达到堆内存上限40M了,因此触发了JVM的GC回收,
导致此前4次创建的软引用所指向的堆内存对象被全部回收;
然后第5/6次创建的对象依旧能够创建完成,
因为此时内存共耗损16M,还没达到内存上限,因此软引用的对象可以继续存活。

弱引用(Weak Reference)

描述非必需对象,但他的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次GC发生之前。当GC发生时,无论内存是否足够,都会回收掉只被弱引用关联的对象。JDK1.2之后,提供了 WeakReference类实现软引用。

  • 例子
代码语言:javascript
复制
/**
 * <p>
 *     弱引用:无论内存是否足够,都会被GC回收
 * </p>
 */

public class WeakReferenceObject {
  public static void main(String[] args) {
    WeakReference weakReference = new WeakReference(new long[1024*1024]);
    System.out.println(weakReference.get());
    System.gc();
    System.out.println(weakReference.get());
  }
}
  • 输出
代码语言:javascript
复制
[J@312b1dae
null

虚引用(Phantom Reference)

幽灵引用或幻影引用,是最弱的一种引用关系。一个对象是否有虚引用,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的,就是能够在这个对象被GC回收时,收到一个系统通知。JDK1.2之后,提供了 PhantomReference类实现软引用。

  • 例子
代码语言:javascript
复制
/**
 * <p>
 *     虚引用/幻影引用:
 *       1)无法通过虚引用获取对一个对象的真实引用
 *       2)虚引用必须通过与 PhantomReference 组合一起使用。当GC  回收一个对象,发现它还有虚引用,就会在回收前把这个引用加到与之关联的ReferenceQueue中
 *         NIO运用到它管理堆外存。
 * </p>
 */

public class ReferenceQueueObject {
  public static void main(String[] args) {
    ReferenceQueue referenceQueue = new ReferenceQueue();
    PhantomReference<byte[]> reference = new PhantomReference<byte[]>(new byte[1], referenceQueue);
    System.out.println(reference.get());
  }
}
  • 输出
代码语言:javascript
复制
null

—END—

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后台技术汇 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档