应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

引用类型(Java四种引用类型原理你真的搞明白了吗?)

  引用类型(Java四种引用类型原理你真的搞明白了吗?)
  Java中一共有4种引用类型(其实还有一些其他的引用类型比如FinalReference):强引用、软引用、弱引用、虚引用。
  其中强引用就是我们经常使用的ObjectanewObject();这样的形式,在Java中并没有对应的Reference类。
  本篇文章主要是分析软引用、弱引用、虚引用的实现,这三种引用类型都是继承于Reference这个类,主要逻辑也在Reference中。问题
  在分析前,先抛几个问题?
  1。网上大多数文章对于软引用的介绍是:在内存不足的时候才会被回收,那内存不足是怎么定义的?什么才叫内存不足?
  2。网上大多数文章对于虚引用的介绍是:形同虚设,虚引用并不会决定对象的生命周期。主要用来跟踪对象被垃圾回收器回收的活动。真的是这样吗?
  3。虚引用在Jdk中有哪些场景下用到了呢?Reference
  我们先看下Reference。java中的几个字段publicabstractclassReferencelt;Tgt;{引用的对象privateTreferent;回收队列,由使用者在Reference的构造函数中指定volatileReferenceQueuelt;?superTgt;queue;当该引用被加入到queue中的时候,该字段被设置为queue中的下一个元素,以形成链表结构volatileReferencenext;在GC时,JVM底层会维护一个叫DiscoveredList的链表,存放的是Reference对象,discovered字段指向的就是链表中的下一个元素,由JVM设置transientprivateReferencelt;Tgt;discovered;进行线程同步的锁对象staticprivateclassLock{}privatestaticLocklocknewLock();等待加入queue的Reference对象,在GC时由JVM设置,会有一个java层的线程(ReferenceHandler)源源不断的从pending中提取元素加入到queueprivatestaticReferencelt;Objectgt;pendingnull;}
  一个Reference对象的生命周期如下:
  主要分为Native层和Java层两个部分。
  Native层在GC时将需要被回收的Reference对象加入到DiscoveredList中(代码在referenceProcessor。cpp中
  processdiscoveredreferences方法),然后将DiscoveredList的元素移动到PendingList中(代码在referenceProcessor。cpp中enqueuediscoveredrefhelper方法),PendingList的队首就是Reference类中的pending对象。
  看看Java层的代码privatestaticclassReferenceHandlerextendsThread{。。。publicvoidrun(){while(true){tryHandlePending(true);}}}staticbooleantryHandlePending(booleanwaitForNotify){Referencelt;Objectgt;r;Cleanerc;try{synchronized(lock){if(pending!null){rpending;如果是Cleaner对象,则记录下来,下面做特殊处理crinstanceofCleaner?(Cleaner)r:null;指向PendingList的下一个对象pendingr。discovered;r。discoverednull;}else{如果pending为null就先等待,当有对象加入到PendingList中时,jvm会执行notifyif(waitForNotify){lock。wait();}retryifwaitedreturnwaitForNotify;}}}。。。如果时CLeaner对象,则调用clean方法进行资源回收if(c!null){c。clean();returntrue;}将Reference加入到ReferenceQueue,开发者可以通过从ReferenceQueue中poll元素感知到对象被回收的事件。ReferenceQueuelt;?superObjectgt;qr。queue;if(q!ReferenceQueue。NULL)q。enqueue(r);returntrue;}
  流程比较简单:就是源源不断的从PendingList中提取出元素,然后将其加入到ReferenceQueue中去,开发者可以通过从ReferenceQueue中poll元素感知到对象被回收的事件。
  另外需要注意的是,对于Cleaner类型(继承自虚引用)的对象会有额外的处理:在其指向的对象被回收时,会调用clean方法,该方法主要是用来做对应的资源回收,在堆外内存DirectByteBuffer中就是用Cleaner进行堆外内存的回收,这也是虚引用在java中的典型应用。
  看完了Reference的实现,再看看几个实现类里,各自有什么不同。
  SoftReferencepublicclassSoftReferencelt;Tgt;extendsReferencelt;Tgt;{staticprivatelongclock;privatelongtimestamp;publicSoftReference(Treferent){super(referent);this。timestampclock;}publicSoftReference(Treferent,ReferenceQueuelt;?superTgt;q){super(referent,q);this。timestampclock;}publicTget(){Tosuper。get();if(o!nullamp;amp;this。timestamp!clock)this。timestampclock;returno;}}
  软引用的实现很简单,就多了两个字段:clock和timestamp。clock是个静态变量,每次GC时都会将该字段设置成当前时间。timestamp字段则会在每次调用get方法时将其赋值为clock(如果不相等且对象没被回收)。
  那这两个字段的作用是什么呢?这和软引用在内存不够的时候才被回收,又有什么关系呢?
  这些还得看JVM的源码才行,因为决定对象是否需要被回收都是在GC中实现的。sizetReferenceProcessor::processdiscoveredreflist(DiscoveredListrefslists〔〕,ReferencePolicypolicy,boolclearreferent,BoolObjectClosureisalive,OopClosurekeepalive,VoidClosurecompletegc,AbstractRefProcTaskExecutortaskexecutor){。。。还记得上文提到过的DiscoveredList吗?refslists就是DiscoveredList。对于DiscoveredList的处理分为几个阶段,SoftReference的处理就在第一阶段。。。for(uinti0;ilt;maxnumq;i){processphase1(refslists〔i〕,policy,isalive,keepalive,completegc);}。。。}该阶段的主要目的就是当内存足够时,将对应的SoftReference从refslist中移除。voidReferenceProcessor::processphase1(DiscoveredListamp;refslist,ReferencePolicypolicy,BoolObjectClosureisalive,OopClosurekeepalive,VoidClosurecompletegc){DiscoveredListIteratoriter(refslist,keepalive,isalive);Decidewhichsoftlyreachablerefsshouldbekeptalive。while(iter。hasnext()){iter。loadptrs(DEBUGONLY(!discoveryisatomic()allownullreferent));判断引用的对象是否存活boolreferentisdead(iter。referent()!NULL)amp;amp;!iter。isreferentalive();如果引用的对象已经不存活了,则会去调用对应的ReferencePolicy判断该对象是不时要被回收if(referentisdeadamp;amp;!policygt;shouldclearreference(iter。obj(),softreftimestampclock)){if(TraceReferenceGC){gclogorttygt;printcr(Droppingreference(INTPTRFORMAT:s)bypolicy,(void)iter。obj(),iter。obj()gt;klass()gt;internalname());}RemoveReferenceobjectfromlistiter。remove();MaketheReferenceobjectactiveagainiter。makeactive();keepthereferentarounditer。makereferentalive();iter。movetonext();}else{iter。next();}}。。。}
  refslists中存放了本次GC发现的某种引用类型(虚引用、软引用、弱引用等),而
  processdiscoveredreflist方法的作用就是将不需要被回收的对象从refslists移除掉,refslists最后剩下的元素全是需要被回收的元素,最后会将其第一个元素赋值给上文提到过的Reference。javapending字段。
  ReferencePolicy一共有4种实现:NeverClearPolicy,AlwaysClearPolicy,LRUCurrentHeapPolicy,LRUMaxHeapPolicy。
  其中NeverClearPolicy永远返回false,代表永远不回收SoftReference,在JVM中该类没有被使用,AlwaysClearPolicy则永远返回true,在referenceProcessor。hppsetup方法中中可以设置policy为AlwaysClearPolicy,至于什么时候会用到AlwaysClearPolicy,大家有兴趣可以自行研究。
  LRUCurrentHeapPolicy和LRUMaxHeapPolicy的shouldclearreference方法则是完全相同:boolLRUMaxHeapPolicy::shouldclearreference(oopp,jlongtimestampclock){jlongintervaltimestampclockjavalangrefSoftReference::timestamp(p);assert(intervalgt;0,Sanitycheck);Theintervalwillbezeroiftherefwasaccessedsincethelastscavengegc。if(intervallt;maxinterval){returnfalse;}returntrue;}
  timestampclock就是SoftReference的静态字段clock,
  javalangrefSoftReference::timestamp(p)对应是字段timestamp。如果上次GC后有调用SoftReferenceget,interval值为0,否则为若干次GC之间的时间差。
  maxinterval则代表了一个临界值,它的值在LRUCurrentHeapPolicy和LRUMaxHeapPolicy两种策略中有差异。voidLRUCurrentHeapPolicy::setup(){maxinterval(Universe::getheapfreeatlastgc()M)SoftRefLRUPolicyMSPerMB;assert(maxintervalgt;0,Sanitycheck);}voidLRUMaxHeapPolicy::setup(){sizetmaxheapMaxHeapSize;maxheapUniverse::getheapusedatlastgc();maxheapM;maxintervalmaxheapSoftRefLRUPolicyMSPerMB;assert(maxintervalgt;0,Sanitycheck);}
  其中SoftRefLRUPolicyMSPerMB默认为1000,前者的计算方法和上次GC后可用堆大小有关,后者计算方法和(堆大小上次gc时堆使用大小)有关。
  看到这里你就知道SoftReference到底什么时候被被回收了,它和使用的策略(默认应该是LRUCurrentHeapPolicy),堆可用大小,该SoftReference上一次调用get方法的时间都有关系。WeakReferencepublicclassWeakReferencelt;Tgt;extendsReferencelt;Tgt;{publicWeakReference(Treferent){super(referent);}publicWeakReference(Treferent,ReferenceQueuelt;?superTgt;q){super(referent,q);}}
  可以看到WeakReference在Java层只是继承了Reference,没有做任何的改动。那referent字段是什么时候被置为null的呢?要搞清楚这个问题我们再看下上文提到过的
  processdiscoveredreflist方法:sizetReferenceProcessor::processdiscoveredreflist(DiscoveredListrefslists〔〕,ReferencePolicypolicy,boolclearreferent,BoolObjectClosureisalive,OopClosurekeepalive,VoidClosurecompletegc,AbstractRefProcTaskExecutortaskexecutor){。。。Phase1:将所有不存活但是还不能被回收的软引用从refslists中移除(只有refslists为软引用的时候,这里policy才不为null)if(policy!NULL){if(mtprocessing){RefProcPhase1Taskphase1(this,refslists,policy,truemarksoopsalive);taskexecutorgt;execute(phase1);}else{for(uinti0;ilt;maxnumq;i){processphase1(refslists〔i〕,policy,isalive,keepalive,completegc);}}}else{policyNULLassert(refslists!discoveredSoftRefs,Policymustbespecifiedforsoftreferences。);}Phase2:移除所有指向对象还存活的引用if(mtprocessing){RefProcPhase2Taskphase2(this,refslists,!discoveryisatomic()marksoopsalive);taskexecutorgt;execute(phase2);}else{for(uinti0;ilt;maxnumq;i){processphase2(refslists〔i〕,isalive,keepalive,completegc);}}Phase3:根据clearreferent的值决定是否将不存活对象回收if(mtprocessing){RefProcPhase3Taskphase3(this,refslists,clearreferent,truemarksoopsalive);taskexecutorgt;execute(phase3);}else{for(uinti0;ilt;maxnumq;i){processphase3(refslists〔i〕,clearreferent,isalive,keepalive,completegc);}}returntotallistcount;}voidReferenceProcessor::processphase3(DiscoveredListamp;refslist,boolclearreferent,BoolObjectClosureisalive,OopClosurekeepalive,VoidClosurecompletegc){ResourceMarkrm;DiscoveredListIteratoriter(refslist,keepalive,isalive);while(iter。hasnext()){iter。updatediscovered();iter。loadptrs(DEBUGONLY(falseallownullreferent));if(clearreferent){NULLoutreferentpointer将Reference的referent字段置为null,之后会被GC回收iter。clearreferent();}else{keepthereferentaround标记引用的对象为存活,该对象在这次GC将不会被回收iter。makereferentalive();}。。。}。。。}
  不管是弱引用还是其他引用类型,将字段referent置null的操作都发生在processphase3中,而具体行为是由clearreferent的值决定的。而clearreferent的值则和引用类型相关。ReferenceProcessorStatsReferenceProcessor::processdiscoveredreferences(BoolObjectClosureisalive,OopClosurekeepalive,VoidClosurecompletegc,AbstractRefProcTaskExecutortaskexecutor,GCTimergctimer){NOTPRODUCT(verifyoktohandlereflists());。。。processdiscoveredreflist方法的第3个字段就是clearreferentSoftreferencessizetsoftcount0;{GCTraceTimett(SoftReference,tracetime,false,gctimer);softcountprocessdiscoveredreflist(discoveredSoftRefs,currentsoftrefpolicy,true,isalive,keepalive,completegc,taskexecutor);}updatesoftrefmasterclock();Weakreferencessizetweakcount0;{GCTraceTimett(WeakReference,tracetime,false,gctimer);weakcountprocessdiscoveredreflist(discoveredWeakRefs,NULL,true,isalive,keepalive,completegc,taskexecutor);}Finalreferencessizetfinalcount0;{GCTraceTimett(FinalReference,tracetime,false,gctimer);finalcountprocessdiscoveredreflist(discoveredFinalRefs,NULL,false,isalive,keepalive,completegc,taskexecutor);}Phantomreferencessizetphantomcount0;{GCTraceTimett(PhantomReference,tracetime,false,gctimer);phantomcountprocessdiscoveredreflist(discoveredPhantomRefs,NULL,false,isalive,keepalive,completegc,taskexecutor);}。。。}
  可以看到,对于Softreferences和Weakreferencesclearreferent字段传入的都是true,这也符合我们的预期:对象不可达后,引用字段就会被置为null,然后对象就会被回收(对于软引用来说,如果内存足够的话,在Phase1,相关的引用就会从refslist中被移除,到Phase3时refslist为空集合)。
  但对于Finalreferences和Phantomreferences,clearreferent字段传入的是false,也就意味着被这两种引用类型引用的对象,如果没有其他额外处理,只要Reference对象还存活,那引用的对象是不会被回收的。Finalreferences和对象是否重写了finalize方法有关,不在本文分析范围之内,我们接下来看看Phantomreferences。PhantomReferencepublicclassPhantomReferencelt;Tgt;extendsReferencelt;Tgt;{publicTget(){returnnull;}publicPhantomReference(Treferent,ReferenceQueuelt;?superTgt;q){super(referent,q);}}
  可以看到虚引用的get方法永远返回null,我们看个demo。publicstaticvoiddemo()throwsInterruptedException{ObjectobjnewObject();ReferenceQueuelt;Objectgt;refQueuenewReferenceQueuelt;gt;();PhantomReferencelt;Objectgt;phanRefnewPhantomReferencelt;gt;(obj,refQueue);ObjectobjgphanRef。get();这里拿到的是nullSystem。out。println(objg);让obj变成垃圾objnull;System。gc();Thread。sleep(3000);gc后会将phanRef加入到refQueue中Referencelt;?extendsObjectgt;phanRefPrefQueue。remove();这里输出trueSystem。out。println(phanRefPphanRef);}
  从以上代码中可以看到,虚引用能够在指向对象不可达时得到一个39;通知39;(其实所有继承References的类都有这个功能),需要注意的是GC完成后,phanRef。referent依然指向之前创建Object,也就是说Object对象一直没被回收!
  而造成这一现象的原因在上一小节末尾已经说了:对于Finalreferences和Phantomreferences,clearreferent字段传入的时false,也就意味着被这两种引用类型引用的对象,如果没有其他额外处理,在GC中是不会被回收的。
  对于虚引用来说,从refQueue。remove();得到引用对象后,可以调用clear方法强行解除引用和对象之间的关系,使得对象下次可以GC时可以被回收掉。End
  针对文章开头提出的几个问题,看完分析,我们已经能给出回答:
  1。我们经常在网上看到软引用的介绍是:在内存不足的时候才会回收,那内存不足是怎么定义的?为什么才叫内存不足?
  软引用会在内存不足时被回收,内存不足的定义和该引用对象get的时间以及当前堆可用内存大小都有关系,计算公式在上文中也已经给出。
  2。网上对于虚引用的介绍是:形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。主要用来跟踪对象被垃圾回收器回收的活动。真的是这样吗?
  严格的说,虚引用是会影响对象生命周期的,如果不做任何处理,只要虚引用不被回收,那其引用的对象永远不会被回收。所以一般来说,从ReferenceQueue中获得PhantomReference对象后,如果PhantomReference对象不会被回收的话(比如被其他GCROOT可达的对象引用),需要调用clear方法解除PhantomReference和其引用对象的引用关系。
  3。虚引用在Jdk中有哪些场景下用到了呢?
  DirectByteBuffer中是用虚引用的子类Cleaner。java来实现堆外内存回收的,后续会写篇文章来说说堆外内存的里里外外。

顶尖华裔院士李飞飞如果我获得诺奖,一定是以中国人的身份《病起书怀》中有言:位卑未敢忘忧国。中华儿女的爱国情到底有多深?即便身在远方,也依然心系祖国,李飞飞就是这样的才女。她期望着有一天自己能够获得诺贝尔奖,那时她将以中国人的……大宅门第二部为什么差那么多演员篇此时此刻,我又把《大宅门1》重温了一遍,这已经不知道是第几遍了,而第二部就是看不下去,影视作品中的人物是灵魂,那么人物背后的演员是核心要素,第二部的演员失败也是导致其失败的重要……生死下一秒上映,于震卧底潜伏热血缉毒,双主线设置有点尬动作片《生死下一秒》已经在网络平台上映播出了,该片由杨玉川执导,于震、辛月领衔主演,也是2019年上映的《无毒岛1》的姊妹篇。《生死下一秒》主要讲述了缉毒大队副队长唐朗(于震饰……阿富汗李小龙,日本小小龙,全世界模仿者放在一起谁更像?有一位华人,是中国人的骄傲;他拳如电闪,腿如风,21岁在美国开设武馆,名震一时;27岁又自成一派,创立了截拳道!他就是让全世界风靡的李小龙!李小龙……收入越低的人,越爱刷B站阅读前请点击关注,跟我一起做一枚能赚会花的上班族作者临公子01hr最近收到一条留言:你把视频发B站还不如发知乎或油管,B站上都是没判断力的低龄用户,整天都是二……从未设限!王一博敢于尝试,驾驭百变,能主流,也能回归爱豆王一博哪一个瞬间最让你心动开篇寄:王一博从未给自己设限过,跨圈到破圈,他总在尝试突破,不断提升中!别用这么的标签来定义,他自始至终都在遵从自己的内心,坚持自己的喜欢……被娱圈事件拖下水!网友喊话何炅退出快本前不久吴亦凡的事情随着他被刑拘,大家都觉得之后应该就没什么问题了。不过就在他被刑拘之后,很多网友却开始把矛头对准了其他和他有交集的明星。这其中快本的何炅是被吐槽得最狠的,因为网……于正发文怒斥造谣者,澄清吴谨言不受宠一事,称新人更需机会于正,不管你对其是何种态度,喜欢或不喜欢,有一点不可否认的是:关于宫斗剧,他还有有两把刷子的。不管是之前的杨幂,还是后来的吴谨言,都是因为这类型的剧,成功爆红!在业内,有不少艺……幼儿园老师被爆让小朋友吃屎,相关部门已介入调查近日,云南昆明有家长反映在学校里,老师让小朋友吃屎的问题引爆网络,记者在采访中询问是否吃过?小朋友说:有过两次,是这样吃的并做出了相关手势。相对于一个三岁的小女孩来讲是不……万人迷陈好身份曝光,竟是中戏正高教授,工资和明星相差甚远在娱乐圈,很多人都喜欢立人设,只是一个不小心人设就很容易垮了,什么好爸爸、好老公可能都是虚的。当然,在娱乐圈最容易垮的就是学霸人设了,之前翟天临就因为学霸人设栽了,声称自己是博……张雨绮回应罗志祥出轨内幕是我干的,怎么了?还记得罗志祥出轨门刚曝出时,整个娱乐圈集体失声。张雨绮是第一个公然发视频内涵罗志祥劈腿的女明星。今天,主持人在采访中再提剪八爪鱼事件,张雨绮大方回应:是我干的,怎么……拒不道歉郭敬明,被指抄袭还嘴硬,156人联名撕下遮羞布喜欢与小鲜肉深度合作把s卡当房卡用小身体有大梦想的郭敬明难道真的好这口吗?当花美男何昶希在节目上表现得一塌糊涂而作为评委的郭敬明却依然……
张涵予我想在荧屏上谈个恋爱,可想争取这种角色都不知道去哪找张涵予,一个出场自带硬汉气场的男人。几年前,他曾受美妆品牌方邀请出镜,可他出现在现场后的画风像是前来突击检查。介绍产品时,像是在盘问查抄;挑选口红时,像是在挑……企业网管(在知名IT企业当网管是怎样的一种体验)企业网管(在知名IT企业当网管是怎样的一种体验)在知名IT企业当网管是怎样一种体验什么体验?背锅侠,吃力不讨好,网管是一个特别吃力不讨好的工作,没出问题时你会显得特……郭婞淳夺中国台北东京奥运首金,总成绩236公斤夺冠新的奥运纪北京时间27日,在东京奥运会女子举重59公斤级比赛中,中国台北选手郭婞淳以抓举103公斤、挺举133公斤、总成绩236公斤夺得冠军,这三项均创造了新的奥运纪录。这是中国台北体育……为何胃癌在西方近乎绝迹,但在中日韩却每天都有?告诉你3个原因欧美基本没有胃癌,胃癌是罕见病!四川大学华西医院肿瘤中心副主任李秋曾经在媒体采访中谈到。胃癌几乎在西方绝迹,在亚洲却是一种常见病,几乎每天都有大量的胃癌患者产生,尤其是中……精索静脉曲张的治疗(精索静脉曲张一定要做手术吗?)精索静脉曲张的治疗(精索静脉曲张一定要做手术吗?)精索静脉曲张是一种对男性健康影响很大的疾病,不但会使男性出现一系列不适症状,比如睾丸疼痛、睾丸坠胀等等,而且还会导致男性……坡道定点停车技巧(科目二坡道定点停车与起步满分技巧攻略)坡道定点停车技巧(科目二坡道定点停车与起步满分技巧攻略)对于科目二坡道定点停车与起步这个项目,不少的学员感觉难度上有些大,还没有上坡的时候压力就顿时袭来,如果说上坡了,没……万人迷陈好复出了,连续两天发新剧相关内容,网友大呼喜欢你九年陈好复出了,记忆里的万人迷又来了,42岁的陈好时隔6年又拍电视剧了。10月5日,陈好在社交平台发文:功勋黄旭华单元开播,试问大海碧波,何为以身许国?青丝化作白发,依旧铁马冰河。……秦海璐王新军不办婚礼,知道原因后,才明白王新军为什么那么爱她秦海璐王新军不办婚礼,知道原因后,才明白王新军为什么那么爱她近日,随着综艺节目《妻子的浪漫旅行》第5季的播出,参加综艺的好多对夫妻都被观众和网友们熟知并且对什么是幸福美满……卵泡发育不好怎么办?(卵泡长不大长得慢?)卵泡发育不好怎么办?(卵泡长不大长得慢?)女性想要自然孕育,必须具备三个条件:1、正常的卵巢功能,月经正常,发育良好的卵子。2、畅通的输卵管,为精卵结合提供场所,保……脚踝浮肿(脚脖子水肿一按一个坑该怎么办)脚踝浮肿(脚脖子水肿一按一个坑该怎么办)脚踝水肿按压后出现凹陷,可根据原因进行治疗,具体如下:1、肾脏疾病:如大量蛋白尿,尤其是大量蛋白尿合并低蛋白血症时,常出现脚踝水肿……沙皇炸弹(沙皇氢弹有多恐怖?蘑菇云高度7个珠穆朗玛峰,亚欧大沙皇炸弹(沙皇氢弹有多恐怖?蘑菇云高度7个珠穆朗玛峰,亚欧大陆漂移9毫米)在上世纪五十年代,赫鲁晓夫执政时期,苏联人民日益热衷于洲际导弹和热核武器,他们认为今后一切力量的……黄磊晒自制晚餐,瞬间被点赞过万,网友黄小厨的画风变了黄磊晒自制晚餐,瞬间被点赞过万,网友:黄小厨的画风变了!在众多的综艺节目里,《向往的生活》一直被网友们津津乐道,也一直在翘盼新一期的来临。终于,在上周五,迎来了《向往的生……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网