热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

详解JAVA弱引用

这篇文章主要介绍了JAVA弱引用的相关资料,帮助大家更好的理解和学习java引用对象,感兴趣的朋友可以了解下

定义

弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型。在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收。

说明

弱引用,从名字来看就很弱嘛,这种引用指向的对象,一旦在GC时被扫描到,就逃脱不了被回收的命运。

但是,弱引用指向的对象也并不一定就马上会被回收,如果弱引用对象较大,直接进到了老年代,那么就可以苟且偷生到Full GC触发前,所以弱引用对象也可能存在较长的一段时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个引用队列中(如果有的话)。

弱引用对应的类为WeakReference,举个栗子:

String s = new String("Frank"); 
WeakReference weakRef = new WeakReference(s);
s = null;

这里我们把s设置为null后,字符串对象便只有弱引用指向它。

弱可达

如果一个对象与GC Roots之间仅存在弱引用,则称这个对象为弱可达(weakly reachable)对象。

注意

在垃圾回收器回收一个对象前,WeakReference类所提供的get方法会返回其引用对象的强引用,一旦垃圾回收器回收掉该对象之后,get方法将返回null。所以在获取弱引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。

下面的代码会让s再次持有对象的强引用:

s = weakRef.get();

如果在weakRef包裹的对象被回收前,用强引用关联该对象,那这个对象又会变成强可达状态。

来看一个简单的栗子了解一下WeakReference引用的对象是何时被回收的:

public class WeakReferenceTest {
 private static final List TEST_DATA = new LinkedList<>();
 private static final ReferenceQueue QUEUE = new ReferenceQueue<>();

 public static void main(String[] args) {
  TestClass obj = new TestClass("Test");
  WeakReference weakRef = new WeakReference<>(obj, QUEUE);
  //可以重新获得OOMClass对象,并用一个强引用指向它
  //oomObj = weakRef.get();

  // 该线程不断读取这个弱引用,并不断往列表里插入数据,以促使系统早点进行GC
  new Thread(() -> {
   while (true) {
    TEST_DATA.add(new byte[1024 * 100]);
    try {
     Thread.sleep(1000);
    } catch (InterruptedException e) {
     e.printStackTrace();
     Thread.currentThread().interrupt();
    }
    System.out.println(weakRef.get());
   }
  }).start();

  // 这个线程不断读取引用队列,当弱引用指向的对象呗回收时,该引用就会被加入到引用队列中
  new Thread(() -> {
   while (true) {
    Reference<&#63; extends TestClass> poll = QUEUE.poll();
    if (poll != null) {
     System.out.println("--- 弱引用对象被jvm回收了 ---- " + poll);
     System.out.println("--- 回收对象 ---- " + poll.get());
    }
   }
  }).start();

  //将强引用指向空指针 那么此时只有一个弱引用指向TestClass对象
  obj = null;

  try {
   Thread.currentThread().join();
  } catch (InterruptedException e) {
   e.printStackTrace();
   System.exit(1);
  }
 }

 static class TestClass {
  private String name;

  public TestClass(String name) {
   this.name = name;
  }

  @Override
  public String toString() {
   return "TestClass - " + name;
  }
 }
}

设置一下虚拟机参数:

-verbose:gc -Xms4m -Xmx4m -Xmn2m

运行结果如下:

[GC (Allocation Failure) 1017K->464K(3584K), 0.0014345 secs]
[GC (Allocation Failure) 1483K->536K(3584K), 0.0017221 secs]
[GC (Allocation Failure) 1560K->648K(3584K), 0.0036572 secs]
TestClass - Test
TestClass - Test
TestClass - Test
[GC (Allocation Failure) 1621K->984K(3584K), 0.0011455 secs]
--- 弱引用对象被jvm回收了 ---- java.lang.ref.WeakReference@51a947fe
--- 回收对象 ---- null
null
...省略n个null和几次GC信息
[Full GC (Ergonomics) 2964K->2964K(3584K), 0.0025450 secs]
[Full GC (Allocation Failure) 2964K->2964K(3584K), 0.0021907 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid6860.hprof ...
Heap dump file created [3912229 bytes in 0.011 secs]
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
at weakhashmap.WeakReferenceTest.lambda$main$0(WeakReferenceTest.java:22)
at weakhashmap.WeakReferenceTest$$Lambda$1/764977973.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)

可以看到,其实弱引用也并不是一发生GC就被回收掉了。

应用场景

如果一个对象仅仅是偶尔使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 WeakReference 来引用该对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

一般来说,很少直接使用WeakReference,而是使用WeakHashMap。在WeakHashMap中,内部有一个引用队列,插入的元素会被包裹成WeakReference,并加入队列中,用来做缓存再合适不过。

在Tomcat的缓存中,其实就用到了WeakHashMap:

public final class ConcurrentCache {
 private final int size;
 private final Map eden;
 private final Map longterm;

 public ConcurrentCache(int size) {
  this.size = size;
  this.eden = new ConcurrentHashMap<>(size);
  this.lOngterm= new WeakHashMap<>(size);
 }

 public V get(K k) {
  // 先从eden中取
  V v = this.eden.get(k);
  if (v == null) {
   // 如果取不到再从longterm中取
   synchronized (longterm) {
    v = this.longterm.get(k);
   }
   // 如果取到则重新放到eden中
   if (v != null) {
    this.eden.put(k, v);
   }
  }
  return v;
 }

 public void put(K k, V v) {
  if (this.eden.size() >= size) {
   // 如果eden中的元素数量大于指定容量,将所有元素放到longterm中
   synchronized (longterm) {
    this.longterm.putAll(this.eden);
   }
   this.eden.clear();
  }
  this.eden.put(k, v);
 }
}

这里有eden和longterm的两个map,如果对jvm堆了解的话,可以看出tomcat在这里是使用ConcurrentHashMap和WeakHashMap做了类似分代缓存的操作。

在put方法里,在插入键值对时,先检查eden缓存的容量是否超出设定的大小。如果没有则直接放入eden缓存,如果超了则锁定longterm将eden中所有的键值对都放入longterm。再将eden清空并插入该键值对。

在get方法中,也是优先从eden中找对应的key,如果没有则进入longterm缓存中查找,找到后就加入eden缓存并返回。

经过这样的设计,相对常用的对象都能在eden缓存中找到,不常用(有可能被销毁的对象)的则进入longterm缓存。而longterm的key的实际对象没有其他引用指向它时,gc就会自动回收heap中该弱引用指向的实际对象,并将弱引用放入其引用队列中。

弱引用与软引用对比

弱引用与软引用的区别在于:

  1. 只具有弱引用的对象拥有更短暂的生命周期。
  2. 被垃圾回收器回收的时机不一样,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。而被软引用关联的对象只有在内存不足时才会被回收。
  3. 弱引用不会影响GC,而软引用会一定程度上对GC造成影响。

相似之处:都是用来描述非必需对象的。

那么什么时候用SoftReference,什么时候用WeakReference呢?

如果缓存的对象是比较大的对象,使用频率相对较高的对象,那么使用SoftReference会更好,因为这样能让缓存对象有更长的生命周期。

如果缓存对象都是比较小的对象,使用频率一般或者相对较低,那么使用WeakReference会更合适。

当然,如果实在不知道选哪个,一般而言,用作缓存时使用WeakHashMap都不会有太大问题。

小结

  • 弱引用是比软引用更弱的引用类型
  • 弱引用不能延长对象的生命周期,一旦对象只剩下弱引用,它就随时可能会被回收
  • 可以通过弱引用获取对象的强引用
  • 弱引用适合用作缓存

以上就是详解 JAVA 弱引用的详细内容,更多关于java 弱引用的资料请关注其它相关文章!


推荐阅读
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了关于apache、phpmyadmin、mysql、php、emacs、path等知识点,以及如何搭建php环境。文章提供了详细的安装步骤和所需软件列表,希望能帮助读者解决与LAMP相关的技术问题。 ... [详细]
  • PHPMailer邮件类邮件发送功能的使用教学及注意事项
    本文介绍了使用国外开源码PHPMailer邮件类实现邮件发送功能的简单教学,同时提供了一些注意事项。文章涵盖了字符集设置、发送HTML格式邮件、群发邮件以及避免类的重定义等方面的内容。此外,还提供了一些与PHP相关的资源和服务,如传奇手游游戏源码下载、vscode字体调整、数据恢复、Ubuntu实验环境搭建、北京爬虫市场、进阶PHP和SEO人员需注意的内容。 ... [详细]
  • 标题: ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文总结了淘淘商城项目的功能和架构,并介绍了传统架构中遇到的session共享问题及解决方法。淘淘商城是一个综合性的B2C平台,类似京东商城、天猫商城,会员可以在商城浏览商品、下订单,管理员、运营可以在平台后台管理系统中管理商品、订单、会员等。商城的架构包括后台管理系统、前台系统、会员系统、订单系统、搜索系统和单点登录系统。在传统架构中,可以采用tomcat集群解决并发量高的问题,但由于session共享的限制,集群数量有限。本文探讨了如何解决session共享的问题。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • MySQL语句大全:创建、授权、查询、修改等【MySQL】的使用方法详解
    本文详细介绍了MySQL语句的使用方法,包括创建用户、授权、查询、修改等操作。通过连接MySQL数据库,可以使用命令创建用户,并指定该用户在哪个主机上可以登录。同时,还可以设置用户的登录密码。通过本文,您可以全面了解MySQL语句的使用方法。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 如何实现JDK版本的切换功能,解决开发环境冲突问题
    本文介绍了在开发过程中遇到JDK版本冲突的情况,以及如何通过修改环境变量实现JDK版本的切换功能,解决开发环境冲突的问题。通过合理的切换环境,可以更好地进行项目开发。同时,提醒读者注意不仅限于1.7和1.8版本的转换,还要适应不同项目和个人开发习惯的需求。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 本文介绍了在sqoop1.4.*版本中,如何实现自定义分隔符的方法及步骤。通过修改sqoop生成的java文件,并重新编译,可以满足实际开发中对分隔符的需求。具体步骤包括修改java文件中的一行代码,重新编译所需的hadoop包等。详细步骤和编译方法在本文中都有详细说明。 ... [详细]
author-avatar
麦豪小仙
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有