PhantomReference虚引用在分析堆外内存回收之前,先了解下PhantomReference
虚引用 。
PhantomReference
需要与ReferenceQueue
引用队列结合使用,在GC进行垃圾回收的时候 , 如果发现一个对象只有虚引用在引用它,则认为该对象需要被回收,会将引用该对象的虚引用加入到与其关联的ReferenceQueue
队列中 , 开发者可以通过ReferenceQueue
获取需要被回收的对象,然后做一些清理操作,从队列中获取过的元素会从队列中清除 , 之后GC就可以对该对象进行回收 。
虚引用提供了一种追踪对象垃圾回收状态的机制,让开发者知道哪些对象准备进行回收,在回收之前开发者可以进行一些清理工作,之后GC就可以将对象进行真正的回收 。
来看一个虚引用的使用例子:
- 创建一个
ReferenceQueue
队列queue,用于跟踪对象的回收; - 创建一个obj对象,通过new创建的是强引用,只要强引用存在,对象就不会被回收;
- 创建一个虚引用
PhantomReference
,将obj对象和ReferenceQueue
队列传入,此时phantomReference里面引用了obj对象,并关联着引用队列queue; - 同样的方式创建另一个obj1对象和虚引用对象phantomReference1;
- 将obj置为NULL,此时强引用关系失效;
- 调用
System.gc()
进行垃圾回收; - 由于obj的强引用关系失效 , 所以GC认为该对象需要被回收 , 会将引用该对象的虚引用phantomReference对象放入到与其关联的引用队列queue中;
- 通过
poll
从引用队列queue中获取对象,可以发现会获取到phantomReference对象,poll
获取之后会将对象从引用队列中删除 , 之后会被垃圾回收器回收; - obj1的强引用关系还在,所以从queue中并不会获取到;
public static void main(String[] args) {// 创建引用队列ReferenceQueue<Object> queue = new ReferenceQueue<Object>();// 创建obj对象Object obj = new Object();// 创建虚引用,虚引用引用了obj对象,并与queue进行关联PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj, queue);// 创建obj1对象Object obj1 = new Object();PhantomReference<Object> phantomReference1 = new PhantomReference<Object>(obj1, queue);// 将obj置为NULL,强引用关系失效obj = null;// 垃圾回收System.gc();// 从引用队列获取对象Object o = queue.poll();if (null != o) {System.out.println(o.toString());}}
输出结果:java.lang.ref.PhantomReference@277c0f21
文章插图
Reference实例的几种状态
Active:初始状态,创建一个Reference类型的实例之后处于Active状态,以上面虚引用为例,通过new创建一个
PhantomReference
虚引用对象之后,虚引用对象就处于Active状态 。Pending:当GC检测到对象的可达性发生变化时,会根据是否关联了引用队列来决定是否将状态更改为Pending或者Inactive,虚引用必须与引用队列结合使用,所以对于虚引用来说,如果它实际引用的对象需要被回收,垃圾回收器会将这个虚引用对象加入到一个Pending列表中,此时处于Pending状态 。
同样以上面的的虚引用为例,因为obj的强引用关系失效,GC就会把引用它的虚引用对象放入到pending列表中 。
Enqueued:表示引用对象被加入到了引用队列,Reference有一个后台线程去检测是否有处于Pending状态的引用对象 , 如果有会将引用对象加入到与其关联的引用队列中,此时由Pending转为Enqueued状态表示对象已加入到引用队列中 。
Inactive:通过引用队列的
poll
方法可以从引用队列中获取引用对象,同时引用对象会从队列中移除,此时引用对象处于Inactive状态,之后会被GC回收 。DirectByteBuffer堆外内存回收在
DirectByteBuffer
的构造函数中,在申请内存之前,先调用了Bits
的reserveMemory
方法回收内存,申请内存之后,调用Cleaner
的create
方法创建了一个Cleaner
对象 , 并传入了当前对象(DirectByteBuffer)和一个Deallocator
类型的对象:class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {private final Cleaner cleaner;DirectByteBuffer(int cap) {// package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));// 清理内存Bits.reserveMemory(size, cap);long base = 0;try {// 分配内存base = unsafe.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}// 创建Cleader,传入了当前对象和Deallocatorcleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 怎么删除微信好友(一键恢复已删除好友)
- 浅谈-动态路由之OSPF的理解
- Jupyter,Matplotlib,Pandas 【机器学习】利用 Python 进行数据分析的环境配置 Windows
- 奇迹暖暖幽夜魅影怎么搭配
- 摩尔庄园10月28日神奇密码是多少
- 荣耀magic3支持鸿蒙系统吗_荣耀magic3能升级鸿蒙吗
- 可持久化数组 P3919 【模板】可持久化线段树 1
- 斗罗大陆武魂觉醒荒野行纪2
- 祖玛阁5去6怎么走(祖玛阁杂货店到6层怎么走)
- 【nginx】使用 nginx 时,使用 sub_filter 注入 js 代码,例如 google analysis 等