关于垃圾回收:如何选择一款适合自己应用的垃圾收集器呢

垃圾收集器选型因素应用程序的次要关注点是什么?如果是数据分析、科学计算类的工作,指标是尽快算出后果,那吞吐量就是次要关注点;如果是SLA利用,那进展工夫间接影响服务质量,重大的甚至会导致事物超时,这样提早就是次要的关注点;而如果是客户端利用或者嵌入式应用,那垃圾收集的内存占用则是侧重点。运行利用的基础设施如何?譬如硬件规格,要设计的零碎时x86-32/64、SPARC还是ARM/Aarch64;处理器的数量是多少,调配的内存大小;抉择的操作系统是Linux、Solaris还是Windos等。应用的JDK的发行版是什么?版本号是多少?是ZingJDK/Zulu、OracleJDK、OpenJDK、OpenJ9抑或是其余公司的发行版?该JDK对应了《Java虚拟机标准》的哪个版本?一般来说收集器的抉择就从以上几点思考,举个例子,假如某个间接面向用户提供服务的B/S零碎筹备抉择垃圾收集器,一般来说延迟时间是这类利用的次要关注点,那么: 如果你有短缺的估算单没有太多调优教训,那么一套代商业技术支持的专有硬件或者软件解决方案是不错的抉择,Azul公司以前主推的Vega零碎和当初主推的Zing VM是这方面的代表,这样你就能够应用传说中的C4收集器了。如果你尽管没有足够估算去应用商业解决方案,但可能掌控软硬件型号,应用较新的版本,同时又特地重视提早,那ZGC很值得尝试。如果你对还处于试验状态的收集器的稳定性有所顾虑,或者利用必须运行在Windos操作系统下,那ZGC就无缘了,试试Shenandoah吧。如果你接手的是遗留零碎,软硬件基础设施和JDK版本都比较落后,那就依据内存规模掂量一下,对于大略4GB到6GB以下的堆内存,CMS个别能解决的比拟好,而对于更大的堆内存,可重点考查一下G1。

April 27, 2023 · 1 min · jiezi

关于垃圾回收:你真的理解Java垃圾回收吗万字长文带你彻底搞懂垃圾回收机制

本文已参加掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力打算|创作者训练营第三期正在进行,「写」出集体影响力。 Java 垃圾回收机制垃圾回收次要关注 Java 堆 Java 内存运行时区域中的程序计数器、虚拟机栈、本地办法栈随线程而生灭;栈中的栈帧随着办法的进入和退出而井井有条地执行着出栈和入栈操作。每一个栈帧中调配多少内存基本上是在类构造确定下来时就已知的(只管在运行期会由 JIT 编译器进行一些优化),因而这几个区域的内存调配和回收都具备确定性,不须要过多思考回收的问题,因为办法完结或者线程完结时,内存天然就跟随着回收了。 而 Java 堆不一样,一个接口中的多个实现类须要的内存可能不一样,一个办法中的多个分支须要的内存也可能不一样,咱们只有在程序处于运行期间时能力晓得会创立哪些对象,这部分内存的调配和回收都是动静的,垃圾收集器所关注的是这部分内存。 判断哪些对象须要被回收有以下两种办法: 援用计数法给对象增加一援用计数器,被援用一次计数器值就加 1;当援用生效时,计数器值就减 1;计数器为 0 时,对象就是不可能再被应用的,简略高效,毛病是无奈解决对象之间互相循环援用的问题。可达性剖析算法通过一系列的称为 "GC Roots" 的对象作为起始点,从这些节点开始向下搜寻,搜寻所走过的门路称为援用链(Reference Chain),当一个对象到 GC Roots 没有任何援用链相连时,则证实此对象是不可用的。此算法解决了上述循环援用的问题。在Java语言中,可作为 GC Roots 的对象包含上面几种:a. 虚拟机栈(栈帧中的本地变量表)中援用的对象。b. 办法区中类动态属性援用的对象。c. 办法区中常量援用的对象。d. 本地办法栈中 JNI(Native办法)援用的对象 作为 GC Roots 的节点次要在全局性的援用与执行上下文中。要明确的是,tracing gc必须以以后存活的对象集为 Roots,因而必须选取确定存活的援用类型对象。 GC 治理的区域是 Java 堆,虚拟机栈、办法区和本地办法栈不被 GC 所治理,因而选用这些区域内援用的对象作为 GC Roots,是不会被 GC 所回收的。 其中虚拟机栈和本地办法栈都是线程公有的内存区域,只有线程没有终止,就能确保它们中援用的对象的存活。而办法区中类动态属性援用的对象是显然存活的。常量援用的对象在以后可能存活,因而,也可能是 GC roots 的一部分。 强、软、弱、虚援用JDK1.2 以前,一个对象只有被援用和没有被援用两种状态。 起初,Java 对援用的概念进行了裁减,将援用分为强援用(Strong Reference)、软援用(Soft Reference)、弱援用(Weak Reference)、虚援用(Phantom Reference)4 种,这 4 种援用强度顺次逐步削弱。 强援用就是指在程序代码之中普遍存在的,相似"Object obj=new Object()"这类的援用,垃圾收集器永远不会回收存活的强援用对象。软援用:还有用但并非必须的对象。在零碎 将要产生内存溢出异样之前 ,将会把这些对象列进回收范畴之中进行第二次回收。弱援用也是用来形容非必须对象的,被弱援用关联的对象 只能生存到下一次垃圾收集产生之前 。当垃圾收集器工作时,无论内存是否足够,都会回收掉只被弱援用关联的对象。虚援用是最弱的一种援用关系。 无奈通过虚援用来获得一个对象实例 。为一个对象设置虚援用关联的惟一目标就是能在这个对象被收集器回收时收到一个零碎告诉。 可达性剖析算法不可达的对象将临时处于“缓刑”阶段,要真正宣告一个对象死亡,至多要经验两次标记过程: 如果对象在进行可达性剖析后发现没有与 GC Roots 相连接的援用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 办法。当对象没有笼罩 finalize() 办法,或者 finalize() 办法曾经被虚拟机调用过,虚拟机将这两种状况都视为“没有必要执行”,间接进行第二次标记。如果这个对象被断定为有必要执行 finalize() 办法,那么这个对象将会搁置在一个叫做 F-Queue 的队列之中,并在稍后由一个由虚拟机主动建设的、低优先级的 Finalizer 线程去执行它。这里所谓的“执行”是指虚构机会触发这个办法,但并不承诺会期待它运行完结,因为如果一个对象在 finalize() 办法中执行迟缓,将很可能会始终阻塞 F-Queue 队列,甚至导致整个内存回收零碎解体。 ...

August 30, 2021 · 13 min · jiezi

关于垃圾回收:九神带你入门JVM下

咱们接着下面一篇持续学习JVM的基本知识。 对象存活判断上篇中咱们介绍过JVM垃圾回收综述中说过一次垃圾回收之后会有一些对象存活。这节咱们介绍两个判断对象存活的算法。 判断对象存活有援用计数算法和可达性剖析算法。 1、援用计数算法 给每一个对象增加一个援用计数器,每当有一个中央援用它时,计数器值加1;每当有一个中央不再援用它时,计数器值减1,这样只有计数器的值不为0,就阐明还有中央援用它,它就不是无用的对象。 这种办法看起来非常简单,但目前许多支流的虚拟机都没有选用这种算法来治理内存,起因就是当某些对象之间相互援用时,无奈判断出这些对象是否已死。如下图,对象1和对象2都没有被堆外的变量援用,而是被对方相互援用,这时他们尽管没有用途了,然而援用计数器的值依然是1,无奈判断他们是死对象,垃圾回收器也就无奈回收。 2、可达性剖析算法 理解可达性剖析算法之前先理解一个概念——GC Roots,垃圾收集的终点,能够作为GC Roots的有虚拟机栈中本地变量表中援用的对象、办法区中动态属性援用的对象、办法区中常量援用的对象、本地办法栈中JNI(Native办法)援用的对象。 当一个对象到GC Roots没有任何援用链相连(GC Roots到这个对象不可达)时,就阐明此对象是不可用的,是死对象。如下图:object1、object2、object3、object4和GC Roots之间有可达门路,这些对象不会被回收,但object5、object6、object7到GC Roots之间没有可达门路,这些对象就是死对象。 下面被断定为非存活的死对象(object5、object6、object7)并不是必死无疑,还有解救的余地。进行可达性剖析后对象和GC Roots之间没有援用链相连时,对象将会被进行一次标记,接着会判断如果对象没有笼罩Object的finalize()办法或者finalize()办法曾经被虚拟机调用过,那么它们就会革除;如果对象笼罩了finalize()办法且还没有被调用,则会执行finalize()办法中的内容,所以在finalize()办法中如果从新与GC Roots援用链上的对象关联就能够援救本人。当然,理论中个别不会这么做。 GC算法接下来讲GC的算法,次要有标记-革除算法、复制算法、标记-整顿算法、分代收集算法。 1、标记-革除算法 最根底的收集算法是“标记-革除”(Mark-Sweep)算法,分两个阶段:首先标记出所有须要回收的对象,在标记实现后对立回收所有被标记的对象。 长处:不须要进行对象的挪动,并且仅对不存活的对象进行解决,在存活对象比拟多的状况极为无效。 有余:一个是效率问题,标记和革除两个过程的效率都不高;另一个是空间问题,标记革除之后会产生大量不间断的内存碎片,空间碎片太多可能导致当前在程序运行过程须要调配较大对象时,无奈找到足够的间断内存而不得不提前触发另一个的垃圾收集动作。 上面两张图从两个角度说明了标记-分明算法: 2、复制算法 为了解决效率问题,一种称为复制(Copying)的收集算法呈现了,它将可用内存按容量划分为大小相等的两块,每次只应用其中的一块。当这一块内存用完了,就将还存活着的对象复制到另外一块上,而后再把曾经应用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存调配时也就不必思考内存碎片等简单状况,只有挪动堆顶指针,按程序分配内存即可,实现简略,运行高效。代价是内存放大为原来的一半。 复制算法过程如上面两张图示意: 商业虚拟机用这个回收算法来回收新生代。IBM钻研表明98%的对象是“朝生夕死“,不须要依照1-1的比例来划分内存空间,而是将内存分为一块较大的”Eden“空间和两块较小的Survivor空间,每次应用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性复制到另外一个Survivor空间上,最初清理掉Eden和方才用过的Survivor空间。Hotspot虚拟机默认Eden和Survivor的比例是8-1.即每次可用整个新生代的90%, 只有一个survivor,即1/10被”节约“。当然,98%的对象回收只是个别场景下的数据,咱们没有方法保障每次回收都只有不多于10%的对象存活,当Survivor空间不够时,须要依赖其余内存(老年代)进行调配担保(Handle Promotion). 如果另外一块survivor空间没有足够空间寄存上一次新生代收集下来的存活对象时,这些对象将间接通过调配担保机制进入老年代。 上面大略介绍一下这个eden survivor复制的过程。 Eden Space字面意思是伊甸园,对象被创立的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。 Survivor Space幸存者区,用于保留在eden space内存区域中通过垃圾回收后没有被回收的对象。Survivor有两个,别离为To Survivor、 From Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全副开释),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),而后To Survivor 和 From Survivor的标记会调换,始终保障一个survivor是空的。 为啥须要两个survivor?因为须要一个残缺的空间来复制过去。当满的时候降职。每次都往标记为to的外面放,而后调换,这时from曾经被清空,能够当作to了。 3、标记-整顿算法 复制收集算法在对象成活率较高时就要进行较多的复制操作,效率将会变低。更要害的是,如果不想节约50%的空间,就须要有额定的空间进行调配担保,以应答被应用的内存中所有对象都100%存活的极其状况,所以,老年代个别不能间接选用这种算法。 依据老年代的特点,有人提出一种”标记-整顿“Mark-Compact算法,标记过程依然和标记-革除一样,但后续步骤不是间接对可回收对象进行清理,而是让所有存活的对象都向一端挪动,而后间接清理端边界以外的内存。 上面两张图讲了这个算法的过程: 4、分代收集算法 以后商业虚拟机的垃圾收集都采纳”分代收集“(Generational Collection)算法,这种算法依据对象存活周期的不同将内存划分为几块。个别把Java堆分为新生代和老年代,这样就能够依据各个年代的特点采纳最适当的收集算法。在新生代,每次垃圾收集时都发现少量对象死去,只有大量存活,那就选用复制算法,只须要付出大量存活对象的复制老本就能够实现收集。而老年代中因为对象存活率较高,没有额定的空间对它进行调配担保,就必须应用”标记-清理“和”标记-整顿“算法来进行回收。 这种算法就是咱们在后面JVM垃圾回收综述中讲述的内容。其本质是更为灵便的应用”标记-清理“和”标记-整顿“算法。 常见的GC回收器当初常见的垃圾收集器有如下几种 ...

November 23, 2020 · 2 min · jiezi

关于垃圾回收:垃圾回收的算法与实现pdf

关注“Java后端技术全栈” 回复“面试”获取全套面试材料 什么是GC? GC是Garbage Collection的简称,中文称为“垃圾回收”。 垃圾的回收Garbage Collection的Garbage,也就是“垃圾”,具体指的是什么呢? 在事实世界中,说到垃圾,指的就是那些不读的书、不穿的衣服等。这种状况下的“垃圾”指的是“本人不必的货色”。在GC中,“垃圾”的定义也是如此。GC把程序不必的内存空间视为垃圾。 GC到底会给程序员带来怎么的益处呢? 没有GC的世界在没有GC的世界里,程序员必须本人手动进行内存治理,必须分明地确保必要的内存空间,开释不要的内存空间。 为了省去上述手动内存治理的麻烦,人们钻研开发出了GC。如果把内存治理交给计算机,程序员就不必去想着开释内存了。 有了GC,程序员就不必再去放心因为忘了开释内存等而导致BUG,从而大大加重了累赘。 最近很多小伙伴问我要一些 GC 相干的材料,于是我翻箱倒柜,找到了这本十分经典的电子书——《垃圾回收的算法与实现》。 材料介绍 《垃圾回收的算法与实现》由多位IT界的大佬联结举荐,被称为是“一本书把握主动内存回收的机制!”。全书分为“算法篇”和“实现篇”两大部分。算法篇介绍了各种算法,实现篇介绍了垃圾回收在Python、DalvikVM、Rubinius、V8等几种语言处理程序中的具体实现。配合大量形象的插图和代码,将各个知识点掰开揉碎解说,非常适合入门学习。 如何获取? 1.辨认二维码并关注公众号「Java后端技术全栈」; 2.在公众号后盾回复关键字「962」

November 19, 2020 · 1 min · jiezi

关于垃圾回收:UE4-垃圾回收

UE4引擎为咱们搭建了一套UObject对象零碎,并且退出了垃圾回收机制,使咱们用C++进行游戏开发时更加不便,而且游戏自身也能够极大水平地防止内存透露问题。 UE4引擎采纳了标记-打扫垃圾回收形式,是一种经典的垃圾回收形式。一次垃圾回收分为两个阶段:第一阶段从一个根汇合登程,遍历所有可达对象,遍历实现后就能标记出可达对象和不可达对象了,这个阶段会在一帧内实现;第二阶段会渐进式地清理这些不可达对象,因为不可达的对象将永远不能被拜访到,所以能够分帧清理它们,防止一下子清理很多UObject。比方,Map卸载时就会产生显著的卡顿。 GC产生在游戏线程上,对UObject进行清理,反对多线程GC。 对GC能够设置若干参数,比方MaxObjectsInGame,规定了游戏中最大存在的UObject对象(对编辑器不失效),挪动平台上默认设置了131072。当UObject数量超过这个阈值时,游戏会解体,其余具体参数可见UGarbageCollectionSettings、GarbageCollection.cpp和UnrealEngine.cpp中相干的属性。 下图为标记-打扫的工作原理: 1.1 GC何时进行UE4引擎中GC能够分为被动引发和主动引发两种形式。 1.1.1 被动引发能够在执行一些操作时手动调用GC。比方,卸载一个资源后,立刻调用一次GC进行清理。 游戏中能够调用ForceGarbageCollection来让World下次Tick时进行垃圾回收,也能够间接调用CollectGarbage进行垃圾回收(引擎中大部分状况都用这种形式被动引发)。 1.1.2 主动引发游戏中,大部分的垃圾回收操作都是由UE4引擎主动引发的,一般状况下不须要手动调用GC,这也是现实的GC应用形式。 当World进行Tick时,会调用UEngine::ConditionalCollectGarbage()函数,函数中进行了一些判断,当满足GC条件时,才会执行GC。上面剖析一下ConditionalCollectGarbage的执行逻辑。 UE4 GC流程 和 清理流程 可返回UWA学堂收费查看。

November 13, 2020 · 1 min · jiezi