Java Reference核心原理分析
{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"带着问题,看源码针对性会更强一点、印象会更深刻、并且效果也会更好。所以我先卖个关子,提两个问题(没准下次跳槽时就被问到)。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们可以用ByteBuffer的allocateDirect方法,申请一块堆外内存创建一个DirectByteBuffer对象,然后利用它去操作堆外内存。这些申请完的堆外内存,我们可以回收吗?可以的话是通过什么样的机制回收的?"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大家应该都知道WeakHashMap可以用来实现内存相对敏感的本地缓存,为什么WeakHashMap合适这种业务场景,其内部实现会做什么特殊处理呢?"}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"GC可到达性与JDK中Reference类型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面提到的两个问题,其答案都在JDK的Reference里面。JDK早期版本中并没有Reference相关的类,这导致对象被GC回收后如果想做一些额外的清理工作(比如socket、堆外内存等)是无法实现的,同样如果想要根据堆内存的实际使用情况决定要不要去清理一些内存敏感的对象也是法实现的。为此JDK1.2中引入的Reference相关的类,即今天要介绍的Reference、SoftReference、WeakReference、PhantomReference,还有与之相关的Cleaner、ReferenceQueue、ReferenceHandler等。与Reference相关核心类基本都在java.lang.ref包下面。其类关系如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4a/4ad1ea5f8488ba94c74f0c0deedbfaa4.png","alt":"reference.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中,SoftReference代表软引用对象,垃圾回收器会根据内存需求酌情回收软引用指向的对象。普通的GC并不会回收软引用,只有在即将OOM的时候(也就是最后一次Full GC)如果被引用的对象只有SoftReference指向的引用,才会回收。WeakReference代表弱引用对象,当发生GC时,如果被引用的对象只有WeakReference指向的引用,就会被回收。PhantomReference代表虚引用对象(也有叫幻象引用的,个人认为还是虚引用更加贴切),其是一种特殊的引用类型,不能通过虚引用获取到其关联的对象,但当GC时如果其引用的对象被回收,这个事件程序可以感知,这样我们可以做相应的处理。最后就是最常见强引用对象,也就是通常我们new出来的对象。在继续介绍Reference相关类的源码前,先来简单的看一下GC如何决定一个对象是否可被回收。其基本思路是从GC Root开始向下搜索,如果对象与GC Root之间存在引用链,则对象是可达的,GC会根据是否可到达与可到达性决定对象是否可以被回收。而对象的可达性与引用类型密切相关,对象的可到达性可分为5种。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"强可到达,如果从GC Root搜索后,发现对象与GC Root之间存在强引用链则为强可到达。强引用链即有强引用对象,引用了该对象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"软可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链,但存在软引用链,则为软可到达。软引用链即有软引用对象,引用了该对象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链与软引用链,但有弱引用链,则为弱可到达。弱引用链即有弱引用对象,引用了该对象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虚可到达,如果从GC Root搜索后,发现对象与GC Root之间只存在虚引用链则为虚可到达。虚引用链即有虚引用对象,引用了该对象。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可达,如果从GC Root搜索后,找不到对象与GC Root之间的引用链,则为不可到达。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看一个简单的列子:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8b/8b60c7e32ba011ba09aec69a613b0fe4.png","alt":"objectReach.png","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ObjectA为强可到达,ObjectB也为强可到达,虽然ObjectB对象被SoftReference ObjcetE 引用但由于其还被ObjectA引用所以为强可到达;而ObjectC和ObjectD为弱引用达到,虽然ObjectD对象被PhantomReference ObjcetG引用但由于其还被ObjectC引用,而ObjectC又为弱引用达到,所以ObjectD为弱引用达到;而ObjectH与ObjectI是不可到达。引用链的强弱有关系依次是 强引用 > 软引用 > 弱引用 > 虚引用,如果有更强的引用关系存在,那么引用链到达性,将由更强的引用有关系决定。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Reference核心处理流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JVM在GC时如果当前对象只被Reference对象引用,JVM会根据Reference具体类型与堆内存的使用情况决定是否把对应的Reference对象加入到一个由Reference构成的pending链表上,如果能加入pending链表JVM同时会通知ReferenceHandler线程进行处理。ReferenceHandler线程是在Reference类被初始化时调用的,其是一个守护进程并且拥有最高的优先级。Reference类静态初始化块代码如下:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"static {\n //省略部分代码...\n Thread handler = new ReferenceHandler(tg, \"Reference Handler\");\n handler.setPriority(Thread.MAX_PRIORITY);\n handler.setDaemon(true);\n handler.start();\n //省略部分代码...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而ReferenceHandler线程内部的run方法会不断地从Reference构成的pending链表上获取Reference对象,如果能获取则根据Reference的具体类型进行不同的处理,不能则调用wait方法等待GC回收对象处理pending链表的通知。ReferenceHandler线程run方法源码:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public void run() {\n //死循环,线程启动后会一直运行\n while (true) {\n tryHandlePending(true);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"run内部调用的tryHandlePending源码:"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"static boolean tryHandlePending(boolean waitForNotify) {\n Reference
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.