分享JVM 的四种引用方式

 更新时间:2022年07月08日 08:42:05   作者:cao_xiaobo  
这篇文章主要介绍了分享JVM 的四种引用方式,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下

前言

Java中提供这四种引用类型主要有两个目的:

  • 可以让程序员通过代码的方式决定某些对象的生命周期;
  • 有利于JVM进行垃圾回收

java.lang.ref包下的引用类结构图:

一、强引用

特点:GC时,永远不会被回收

是指创建一个对象并把这个对象赋给一个引用变量。

比如:

Object object = new Object();
String str = "hello"

强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。

public void fun1() {
    Object object = new Object();
    Object[] objArr = new Object[1000];
}

当运行至Object[] objArr = new Object[1000];这句时,如果内存不足,JVM会抛出OOM错误也不会回收object指向的对象。不过要注意的是,当fun1运行完之后,object和objArr都已经不存在了,所以它们指向的对象都会被JVM回收。

如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。比如Vector类的clear方法中就是通过将引用赋值为null来实现清理工作的

强引用也是导致内存泄露的主要原因

二、软引用

特点:内存不足时(自动触发GC),会被回收

如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。

SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对 这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之 后,get()方法将返回null。

示例:

JVM参数 -Xms10m -Xmx10m -XX:+PrintGCDetails

public static void main(String[] args) {
    Object obj = new Object();
    SoftReference<Object> softRef = new SoftReference<Object>(obj);
    System.out.println(obj);
    System.out.println(softRef.get());
    // 对象要设置为null,否则不会被回收。原因:通过设置为null让对象失去引用,方便GC
    // 备注:因为在这个main方法中(主线程),方法未结束之前,不设置为null,对象是不会失去引用的。
    obj = null;
    // 当内存不足时,会自动触发GC操作,这里就无需手动GC
    try {
        byte[] b = new byte[30 * 1024 * 1024];
    } catch (Exception e) {
        // TODO: handle exception
    } finally {
        System.out.println(obj);
        System.out.println(softRef.get());
    }
}

上述示例说明,软引用在内存不够时,通过系统的GC,回收对象了

假如有一个应用需要读取大量的本地图片:

  • 如果每次读取图片都从硬盘中读取则会严重影响性能
  • 如果一次全部加载到内存中又可能会造成内存溢出

此时使用软引用可以解决这个问题。

设计思路:用一个HashMap来保存图片的路径的相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免OOM的问题。

Map<String, SoftRefrence<Bitmap>> imageCache = new HashMap<String, SoftRefrence<Bitmap>>();

三、弱引用

特点:无论内存是否充足,只要进行GC,都会被回收

引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示

示例:

public static void main(String[] args) {
    Object obj = new Object();
    WeakReference<Object> weakRef = new WeakReference<Object>(obj);
    System.out.println(obj);            // java.lang.Object@7852e922
    System.out.println(weakRef.get());    // java.lang.Object@7852e922
    // 对象要设置为null,否则不会被回收。原因:通过设置为null让对象失去引用,方便GC
    // 备注:因为在这个main方法中(主线程),方法未结束之前,不设置为null,对象是不会失去引用的。
    obj = null;
    // 这里通过手动触发GC操作。否则内存充足的情况下很难自动触发GC
    System.gc();

    System.out.println(obj);            // null
    System.out.println(weakRef.get());    // null
}

上述示例表明,在内存充足的情况下,弱引用的对象也被回收了。

WeakHashMap的用法

public static void main(String[] args) {
    WeakHashMap<String, String> weakMap = new WeakHashMap<String, String>();
    String key = "1";
    weakMap.put(key, "test");
    System.out.println(weakMap);            // {1=test}
    System.out.println(weakMap.get(key));    // test

    key = null;
    System.gc();
    System.out.println(weakMap);            // {1=test}
    System.out.println(weakMap.get(key));    // null
}

软引用和弱引用的使用场景:mybatis中的缓存

四、虚引用

特点:如同虚设,和没有引用没什么区别

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

示例:

public static void main(String[] args) {
    Object obj = new Object();
    ReferenceQueue<String> queue = new ReferenceQueue<String>();  
    PhantomReference<String> pr = new PhantomReference<String>(obj, queue);  
    System.out.println(pr.get());  // null
}

虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomRefrence的get方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被GC回收,用来实现比finalization机制更灵活的回收操作。

换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。

引用队列的用法

public static void main(String[] args) {
    Object obj = new Object();
    ReferenceQueue<Object> queue = new ReferenceQueue<Object>();  
    WeakReference<Object> weakRef = new WeakReference<Object>(obj, queue);  
    System.out.println(obj);             // java.lang.Object@7852e922
    System.out.println(weakRef.get());  // java.lang.Object@7852e922
    System.out.println(queue.poll());      // null

    obj = null;
    System.gc();

    System.out.println(obj);             // null
    System.out.println(weakRef.get());  // null
    System.out.println(queue.poll());     // java.lang.ref.WeakReference@4e25154f
}

到此这篇关于分享JVM 的四种引用方式的文章就介绍到这了,更多相关JVM 引用方式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring IOC容器Bean管理的完全注解开发放弃配置文件

    Spring IOC容器Bean管理的完全注解开发放弃配置文件

    这篇文章主要为大家介绍了Spring IOC容器的Bean管理完全注解开发放弃配置文件,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • springboot实现jar运行复制resources文件到指定的目录(思路详解)

    springboot实现jar运行复制resources文件到指定的目录(思路详解)

    这篇文章主要介绍了springboot实现jar运行复制resources文件到指定的目录,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • Java8中CompletableFuture的用法全解

    Java8中CompletableFuture的用法全解

    这篇文章主要给大家介绍了关于Java8中CompletableFuture用法的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-01-01
  • 老生常谈Java异常处理和设计(推荐)

    老生常谈Java异常处理和设计(推荐)

    下面小编就为大家带来一篇老生常谈Java异常处理和设计(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Spring Boot 定义系统启动任务的多种方式

    Spring Boot 定义系统启动任务的多种方式

    这篇文章主要介绍了Spring Boot 定义系统启动任务的多种方式,看看你都会哪几种 ,感兴趣的朋友跟随小编一起看看吧
    2019-04-04
  • java+mysql模拟实现银行系统

    java+mysql模拟实现银行系统

    这篇文章主要为大家详细介绍了java+mysql模拟实现银行系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • SpringBoot项目中枚举类型字段与前端和数据库的交互方法

    SpringBoot项目中枚举类型字段与前端和数据库的交互方法

    最近做的这个项目中,用到了大量的枚举类,下面这篇文章主要给大家介绍了关于SpringBoot项目中枚举类型字段与前端和数据库的交互方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • Java将本地项目部署到Linux服务器的实践

    Java将本地项目部署到Linux服务器的实践

    本文主要介绍了Java将本地项目部署到Linux服务器的实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>
    2022-06-06
  • 如何将字符串、字节数组转为输入流

    如何将字符串、字节数组转为输入流

    这篇文章主要介绍了如何将字符串、字节数组转为输入流问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • MyBatis传入参数为List对象的实现

    MyBatis传入参数为List对象的实现

    这篇文章主要介绍了MyBatis传入参数为List对象的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03

最新评论