• java gc --- 四种引用


    古龙有《七种武器》,java里有四种引用。

    下文主要是对 《understanding-weak-references》这一博文的重点进行翻译

    强引用,strong references

    StringBuffer buffer = new StringBuffer()

    像上面这样的就是强引用,只要某个对象与gc root有强引用链相连,那么这个对象就不可能被gc回收掉。jvm宁可发生OOM,也不会回收这个对象。

    强引用有时可能太强了

    作者提出了两种场景

    1. 假设我们有一个不可扩展的Widget类,现在我们想给每个Widget对象附加一个编号属性,那么我们可以这么做:额外搞一个HashMap,key是Widget对象,value是编号,通过更新与查询这个HashMap,可以实现想要的功能。

    但是这个时候问题来了,如果有的Widget对象不再被使用到了(成为了垃圾),由于HashMap中保存了对所有Widget的强引用,所以GC绝对不会释放这些Widget,那么这个HashMap就会越来越大,与之关联的Widget也会越来越多,直到发生OOM。

    为了解决这个问题,一个很简单的想法当然是“如果有Widget不用了,那就把它从HashMap里删掉啊”,但是如何才能知道某个Widget再不被使用了呢?总不能再弄一个引用计数系统吧。

    2. 假设我们正在构建一个缓存系统,比方说把硬盘里的图片缓存到内存里,但是内存总是有限的,如果我们在内存中保存的是图片对象的强引用,那么这些对象不会在gc中被释放,这很容易就会导致内存被耗尽,然后发生OOM

    弱引用  Weak References

    弱引用不足以让对象在内存中强制驻留。也就是说,被弱引用修饰的对象,在gc过程中可能会被回收,你并不需要什么额外的操作。下面是使用范例:

    WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget);

    我们可以使用weakWidget.get()方法来获取这个弱引用所指向的对象,但是由于这个对象可能已经被gc回收了,所以get到的值可能为null。

    对于上面提出的第一种场景,我们可以使用系统内置的WeakHashMap来完成这一工作,WeakHashMap的特点是它的key值是WeakReference(不是value值!),而且如果WeakHashMap的某个key值被自动回收了,它所对应的value也会被自动释放。场景一的问题就这样被轻松解决了。

    引用队列  Reference Queues

    如果WeakReference的get方法返回null了,那么这个WeakReference所关联的对象已经被释放了,但是这个WeakReference对象本身还是存在的,它会占用空间,为了避免内存泄漏。我们需要一个机制来确保WeakReference也能被释放。

    于是有了ReferenceQueue。

    如果在创建WeakReference的时候,在构造函数里传入一个ReferenceQueue,那么在这个WeakReference所引用的对象被回收之后,这个WeakReference会被自动插入到ReferenceQueue里来。于是我们可以在适当的时候(比方说后台开一个定时线程)去扫描这个ReferenceQueue,然后把队列里的无用的WeakReference全部清除掉。

    软引用 Soft References

    软引用与弱引用极其类似,但是软引用相比之下并不急于释放所引用的对象。

    弱引用不会影响gc,弱引用指向的对象只要被gc扫描到,就会被释放,但是由于gc线程的优先级很低,而且这个对象可能已经被提升到了老年代,所以这个对象还是可能存活很久。

    软引用会影响gc,被软引用指向的对象只有在内存不足的时候,才会被释放掉(generally retained as long as memory is in plentiful supply)。

    我个人的理解就是:软引用指向的对象可以无视young gc,只在full gc里被回收。弱引用的对象碰到young gc也会被回收。

    幻引用 Phantom References

    幻引用的get方法永远返回null(PhantomReference重写了父类Reference的get方法,其get方法里就一行代码:return null),也就是说一旦一个对象被放到幻引用里,就再也不可能通过这个幻引用找到它了。

    它唯一的作用就是在对象被真正回收之后,放到ReferenceQueue里(因此它只有一个带有ReferenceQueue的构造方法)。

    它与WeakReference的区别是:

    WeakReference:一旦探测到对象只有WeakReference,就将其放入ReferenceQueue(WeakReference are enqueued as soon as the object to which they point becomes weakly reachable. This before finalization or garbage collection has actually happened.)

    PhantomReference:只有在对象被真正销毁之后,才会被放入到ReferenceQueue中(PhantomReferences are enqueued only when the object is physically removed from memory.)

    很奇怪,网上很多中文资料里写的都是错的。

    稍微总结一下,按引用强度来划分:

    强引用>软引用>弱引用>幻引用 

    ps: ReferenceQueue还与FinalReference有相当的关联,具体请查看参考资料中的那篇infoq的文章

    参考资料

    https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references

    https://www.zhihu.com/question/49760047/answer/123486092

    http://www.infoq.com/cn/articles/jvm-source-code-analysis-finalreference

  • 相关阅读:
    最深叶节点的最近公共祖先
    ML-Agents(十)Crawler
    ML-Agents(九)Wall Jump
    ML-Agents(八)PushBlock
    ML-Agents(七)训练指令与训练配置文件
    Unity Editor扩展编辑器中显示脚本属性
    ML-Agents(六)Tennis
    数据结构(二)—栈
    ML-Agents(五)GridWorld
    ML-Agents(四)3DBall补充の引入泛化
  • 原文地址:https://www.cnblogs.com/stevenczp/p/6599927.html
Copyright © 2020-2023  润新知