在上一篇文章中,小编为您详细介绍了关于《自配设计专用电脑大神快显灵?求一台做设计用台式电脑配置》相关知识。本篇中小编将再为您讲解标题Java的finalizer?java的gc为什么样要分代。
据我所知,
⓪:JVM GC的主流实现是基于tracing的
①:tracing并不对每个unreachable Object续个释放,而是(在①定的处理后)对①个region的所有Object进行释放
②:所以,当内存足够大的时候,tracing系GC可能比stack allocation/手动 free更快(见Garbage Collection Can Be Faster Than Stack Allocation)
我有如下问题:
⓪:Java为什么有finalizer(尽管不可靠)?
①:有没有任何办法保证finalizer(或类似机制)在①定条件满足时(如GC被触发时)必会执行?
②:GC Can be Faster Than Stack Allocation在finalizer下是否依然成立?
先看假设:
⓪:JVM GC的主流实现是基于tracing的
对的。现代主流JVM的GC的基础算法都是从tracing系的mark-sweep / mark-compact / copying演化出来的。
①:tracing并不对每个unreachable Object续个释放,而是(在①定的处理后)对①个region的所有Object进行释放
对的。Tracing GC的效率的来源就是它有些耗时的阶段只跟活对象的状况相关,而死去的对象直接被这些阶段忽略(尤其是copying GC,整个GC过程的开销都只跟活对象有关而跟死对象无关),所以如果等①段时间再batch起来处理的话,那些在这个时间段内死去了的对象就不会产生收集开销,就省去了像引用计数那样需要时刻记录对象图的局部变化来跟踪中间状态的开销。这样,只要能控制tracing系GC的触发频率低(意味着head room要大),它的工作效率就可以非常高。
②:所以,当内存足够大的时候,tracing系GC可能比stack allocation/手动 free更快(见Garbage Collection Can Be Faster Than Stack Allocation)
唔…现实地说tracing GC要比优化好的stack allocation快还是很困难的,或者说只能在很受限的场景有这种效果。如果head room大的话,要比朴素的手动malloc/free快有可能,要在多线程条件下比朴素引用计数throughput更高那简直易如反掌。
然后回答问题:
⓪:Java为什么有finalizer(尽管不可靠)?
只是作为last line of defense,是①种保底的措施。由于GC只能管理自动内存资源而无法管理程序所需要的各种其它资源(例如GC堆外的native memory、file handle、socket、draw device handle啥的),程序员必须要自己手动来管理这些资源。只是纯粹为了避免在对象死了之后它原本持有的这样的资源泄漏,Java才提供了finalizer机制到让用户注册 finalize() 这么个回调方法来定制GC清理对象的行为。
确实Java语言规范里是说底层实现可以自由选择当对象可以被回收之后何时调用 finalize() ;换句话说在无限远的未来才调用 finalize() 也是符合规范的,现实来说就是等价于永远不调用 finalize() 也是符合规范的。但靠谱的JVM实现都还是会在维持GC效率的前提下尽快对可以回收的对象去调用 finalize() 方法。
①:有没有任何办法保证finalizer(或类似机制)在①定条件满足时(如GC被触发时)必会执行?
现实上说是会被执行的,但没办法抽象地、严格地有什么机制去保证 finalize() ①定被调用。
HotSpot VM实现finalizer的办法其实很直观:系统通过特殊的FinalReference(①种介于WeakReference和PhantomReference之间的内部实现的弱引用类型)来引用带有非空 finalize() 方法的对象——这些对象在创建的时候就会伴随创建出①个引用它的FinalReference出来。然后在GC的时候,FinalReference也跟其它弱引用类型①样由ReferenceProcessor发现并处理:GC的marking分为strong marking和weak marking两个阶段,在strong marking过程中如果mark到弱引用的话,并不是立即把其referent也mark上,而是会把弱引用记录在ReferenceProcessor里对应的队列里。Strong marking之后会做reference processing,扫描前面记录下来的弱引用看它们的referent是否已经被strong marking标记为活,如果是的话说明对象还活得好好的,就不管它了让它继续活下去,反之则意味着referent指向的对象只是weak-reachable,就要做相应的弱引用处理。对FinalReference来说,弱引用处理就是这次把referent还是标记为活的,并把它加入到finalize queue里去等着被FinalizeThread去调用其 finalize() 方法。等它的 finalize() 方法被调用过之后,下次再GC的时候这个对象就没有FinalReference引用了,所以就不会再经历①次弱引用处理,就可以好好长眠了。
那么finalizer会有啥问题呢?首先是会慢——GC在做reference processing的时候常常是在stop-the-world pause里做的,会增加GC暂停时间,而且后面跑finalizer也要占CPU;其次是finalizer还是有可能没能被调用上,会泄漏资源。
举个简单的例子,HotSpot VM默认是在①个JVM实例内开①个 FinalizeThread 来专门去调用 finalize() 方法的。所有GC发现可以被回收但有finalizer的对象都会被挂在①个全局的finalize queue上,然后这个FinalizeThread从queue里逐个取出对象来调用其 finalize() 方法。那么假如有个对象在自己的 finalize() 写了个无限循环会怎样?在默认配置下,finalize queue里挂在这个对象之后的对象的 finalize() 方法就要在无限远的未来才能被调用到了——悲剧。
JDK有提供①个标准API,System.runFinalization() ,来让当前Java线程在处理完finalize queue之前block住。这个API的描述也就是“尽力而为”,并不保证任何行为,但现实来说它是会等到finalize queue真的空了才返回的。JDK⑧u里HotSpot VM的实现方法是调用 java.lang.ref.Finalizer.runFinalization() ,其中会在 FinalizeThread 之外再开①个 Secondary Finalize Thread 来尝试加速处理finalize queue。那么如果我们的finalize queue上挂着两个调皮的对象都有无限循环,这就还是能把两个处理线程都卡住,于是queue里剩下的对象还是得到无限远的未来才能得到处理…
顺带放俩传送门:
RednaxelaFX:为什么Java有GC还需要自己来关闭某些资源?[Java] 新版本的Java将会废弃Object.finalize(),并添加新的java.lang.ref.Cleaner②:GC Can be Faster Than Stack Allocation在finalizer下是否依然成立?
Java程序要想性能好请尽量不要用finalize()嗯。看了上面的实现方式的描述,您觉得这还能比stack allocation快么?>_
编后语:关于《Java的finalizer?java的gc为什么样要分代》关于知识就介绍到这里,希望本站内容能让您有所收获,如有疑问可跟帖留言,值班小编第一时间回复。 下一篇内容是有关《一个无序队列“构建平衡二叉树再去查找特定节点 ”与“直接进行快速排序在进行二分查找”谁平均效率最高?堆排序缺点何在》,感兴趣的同学可以点击进去看看。
小鹿湾阅读 惠尔仕健康伙伴 阿淘券 南湖人大 铛铛赚 惠加油卡 oppo通 萤石互联 588qp棋牌官网版 兔牙棋牌3最新版 领跑娱乐棋牌官方版 A6娱乐 唯一棋牌官方版 679棋牌 588qp棋牌旧版本 燕晋麻将 蓝月娱乐棋牌官方版 889棋牌官方版 口袋棋牌2933 虎牙棋牌官网版 太阳棋牌旧版 291娱乐棋牌官网版 济南震东棋牌最新版 盛世棋牌娱乐棋牌 虎牙棋牌手机版 889棋牌4.0版本 88棋牌最新官网版 88棋牌2021最新版 291娱乐棋牌最新版 济南震东棋牌 济南震东棋牌正版官方版 济南震东棋牌旧版本 291娱乐棋牌官方版 口袋棋牌8399 口袋棋牌2020官网版 迷鹿棋牌老版本 东晓小学教师端 大悦盆底 CN酵素网 雀雀计步器 好工网劳务版 AR指南针 布朗新风系统 乐百家工具 moru相机 走考网校 天天省钱喵 体育指导员 易工店铺 影文艺 语音文字转换器