当前位置: 首页 > news >正文

网站建设应当注意wordpress主题商城

网站建设应当注意,wordpress主题商城,做购物平台网站客户体验活动,网页设计图片外链一、概述 理解Java虚拟机垃圾回收机制的底层原理#xff0c;是成为一个高级Java开发者的基本功。本文从底层的垃圾回收算法开始#xff0c;着重去阐释不同垃圾回收器在算法设计和实现时的一些技术细节#xff0c;去探索「why」这一部分#xff0c;通过对比不同的垃圾回收算…一、概述 理解Java虚拟机垃圾回收机制的底层原理是成为一个高级Java开发者的基本功。本文从底层的垃圾回收算法开始着重去阐释不同垃圾回收器在算法设计和实现时的一些技术细节去探索「why」这一部分通过对比不同的垃圾回收算法和其实现进一步感知目前垃圾回收的发展脉络 本文主要分为上下两个部分 第一部分「基本概念」主要介绍GC的定义流派、名词解释 第二部分为「算法篇」主要介绍一些重要的 GC 算法去领略 GC 独特的思维方式和各算法的特性这些是和具体的无关的 二、基本概念 2.1 GC的定义 首先我们来看一下什么是 GC。 GC 把程序不用的内存空间视为「垃圾」几乎所有的GC 要做的就只有两件事 找到内存空间里的垃圾使其和活对象分开来。回收垃圾对象的内存使得程序可以重复使用这些内存。 GC 给我们带来的好处不言而喻选择 GC 而不是手动释放资源的原因很简单程序比人更可靠。即便是 C/C 这种没有 GC 的语言也有类似 Boehm GC 这样的第三方库来实现内存的自动管理了。可以毫不夸张地说GC 已经是现代编程语言的标配。 2.2 GC 的流派 GC 从其底层实现方式即 GC 算法来看大体可以分为两大类基于可达性分析的 GC和基于引用计数法的 GC。当然这样的分类也不是绝对的很多现代 GC 的设计就融合了引用计数和可达性分析两种。 可达性​​​​​​​分析法 基本思路就是通过根集合gc root作为起始点从这些节点出发根据引用关系​​​​​​​开始搜索所经过的路径称为引用链当一个对象没有被任何引用链访问到时则证明此对象是不活跃的可以被回收。使用此类算法的有JVM、.NET、Golang等。 引用计数法 引用计数法没有用到根集概念。其基本原理是在堆内存中分配对象时会为对象分配一段额外的空间这个空间用于维护一个​​​​​​​计数器如果有一个新的引用指向这个对象则计数器的值加1如果指向该对象的引用被置空或指向其它对象则计数器的值减1。每次有一个新的引用指向这个对象时计数器加1反之如果指向该对象的引用被置空或指向其它对象则计数器减1当计数器的值为0时则自动删除这个对象。使用此类算法的有 Python、Objective-C、Per l等。 基于可达性分析法的 GC 垃圾回收的效率较高实现起来比较简单引用计算法是是算法简单实现较难但是其缺点在于 GC 期间整个应用需要被挂起STWStop-the-world下同后面很多此类算法的提出都是在解决这个问题缩小 STW 时间。 基于引用计数法的 GC天然带有增量特性incrementalGC 可与应用交替运行不需要暂停应用同时在引用计数法中每个对象始终都知道自己的被引用数当计数器为0时对象可以马上回收而在可达性分析类 GC 中即使对象变成了垃圾程序也无法立刻感知直到 GC 执行前始终都会有一部分内存空间被垃圾占用。 上述两类 GC 各有千秋真正的工业级实现一般是这两类算法的组合但是总体来说基于可达性分析的 GC 还是占据了主流究其原因首先引用计数算法无法解决「​​​​​​​循环引用无法回收」的问题即两个对象互相引用所以各对象的计数器的值都是 1即使这些对象都成了垃圾无外部引用GC 也无法将它们回收。当然上面这一点还不是引用计数法最大的弊端引用计数算法最大的问题在于计数器值的增减处理非常繁重譬如对根对象的引用此外多个线程之间共享对象时需要对计数器进行原子递增/递减这本身又带来了一系列新的复杂性和问题计数器对应用程序的整体运行速度的影响这里的细节可以参考文章Boosts shared_ptr up to 10× slower than OCamls garbage collection[1]。 本文后面介绍的垃圾回收算法主要就是可达性分析类算法及其变种。 2.3 垃圾回收核心概念 在深入研究垃圾回收算法的实现细节之前有必要知道 GC 算法中的一些基本概念这对了解 GC 算法的基本原理和演进过程是有帮助的。除了算法基础名词外我们需要深入理解GC 世界里极其重要的两个核心概念读/写屏障和三色标记法。 基础名词 2.3.1 根节点GC Roots 在 GC 的世界里根是执行可达性分析的「起点」部分在 Java 语言中可以作为 GC Roots 的对象包括 虚拟机栈中栈帧中的本地变量表引用的对象方法区中的类静态属性引用的对象方法区中常量引用的对象本地方法栈中 JNINative 方法 引用的对象 是否作为根的判定依据程序是否可以直接引用该对象譬如调用栈中的变量指针。同时还需要注意的是不同的垃圾回收器选择 GC Roots 的范围是不一样的。 2.3.2 并行回收串行回收 根据垃圾回收的运行方式不同GC 可以分为三类 串行执行垃圾回收器执行的时候应用程序挂起串行执行指的是垃圾回收器有且仅有一个后台线程执行垃圾对象的识别和回收并行执行垃圾回收器执行的时候应用程序挂起但是在暂停期间会有多个线程进行识别和回收可以减少垃圾回收时间并发执行垃圾回收器执行期间应用程序不用挂起正常运行当然在某些必要的情况下垃圾回收器还是需要挂起的。 上面并发和并行容易混淆因为在 Java 中我们提到的并发天然会联想到是「同一类多个线程」执行「同一类任务」在 GC 中并发描述的是「GC 线程」和「应用线程」一起工作。 当我们说到某种垃圾回收器支持并发时并不意味着在垃圾回收的过程中都是并发的譬如G1 和 CMS 垃圾回收器支持并发标记但是在对象转移、引用处理、​​​​​​​符号表和字符串表处理、类卸载时是不支持并发的。总之并发的表述具有「阶段性」。 2.3.3 三色标记法 可达性分析类 GC 都属于「搜索型算法」标记阶段经常用到​​​​​​​深度优先搜索这一类算法的过程可以用 Edsger W. Dijkstra 等人提出的三色标记算法Tri-color marking来进行抽象算法详情可以参考论文On-the-fly Garbage CollectionAn Exercise in Cooperation[2]。顾名思义三色标记算法背后的首要原则就是把堆中的对象根据它们的颜色分到不同集合里面这三种颜色和所包含的意思分别如下所示 白色还未被垃圾回收器标记的对象灰色自身已经被标记但其拥有的成员变量还未被标记黑色自身已经被标记且对象本身所有的成员变量也已经被标记 在 GC 开始阶段刚开始所有的对象都是白色的在通过可达性分析时首先会从根节点开始遍历将 GC Roots 直接引用到的对象 A、B、C 直接加入灰色集合然后从灰色集合中取出 A将 A 的所有引用加入灰色集合同时把 A 本身加入黑色集合。最后灰色集合为空意味着可达性分析结束仍在白色集合的对象即为 GC Roots 不可达可以进行回收了。下面是第一轮标记结束后各个对象的颜色分布。 基于可达性分析的 GC 算法标记过程几乎都借鉴了三色标记的算法思想尽管实现的方式不尽相同比如标记的方式有栈、队列、多色指针等。 2.3.4 读屏障写屏障 在标记对象是否存活的过程中对象间的引用关系是不能改变的这对于串行 GC 来说是可行的因为此时应用程序处于 STW 状态。对于并发 GC 来说在分析对象引用关系期间对象间引用关系的建立和销毁是肯定存在的如果没有其他补偿手段并发标记期间就可能出现对象多标和漏标的情况。还是以上面三色标记法中的例子说明 1多标 假设 C 被标为灰色后在进行下面的标记之前A 和 C 之间的引用关系解除了应用程序按照三色标记法C 和 E 都应该是垃圾而事实上C 不会在本轮 GC 活动中被回收这部分本应该回收但是没有回收到的内存被称之为「浮动垃圾」。 2漏标 如下图所示对象 C 在被标记为灰色后对象 C 断开了和对象 E 之间的引用同时对象 A 新建了和对象 E 之间的引用。在进行后面的标记时因为 C 没有对 E 的引用所以不会将 E 放到灰色集合虽然 A 重新引用了 E但因为 A 已经是黑色了不会再返回重新进行深度遍历了。最终导致的结果是对象 E 会一直停留在白色集合中最后被当作垃圾回收事实上 E 却是活动对象这种情况也是不可接受的。 多标不会影响程序的正确性只会推迟垃圾回收的时机漏标会影响程序的正确性需要引入读写屏障来解决漏标的问题。GC 里的读屏障Read barrier和写屏障Write barrier指的是程序在从堆中读取引用或更新堆中引用时GC 需要执行一些额外操作其本质是一些同步的指令操作在进行读/写引用时会额外执行这些指令。读/写屏障实现的是「对读/写引用这个操作的环切」即该操作前后都在屏障的范畴内可以将读/写屏障类比于 Spirng 框架里的​​​​​​​拦截器。下面所示的代码当从 foo 的成员变量第一次从堆上被加载时就会触发读屏障后续使用该引用不会触发 而当 bar 的成员变量(引用类型的)被分配/写入时会触发写屏障。 void example(Foo foo) { Bar bar foo.bar; // 这里触发读屏障 bar.otherObj makeOtherValue(); // 这里触发写屏障 } 读写屏障是如何解决并发标记时的漏标的总结一下发生漏标的​​​​​​​充分必要条件是 应用线程插入了一个从黑色对象A到白色对象E的新引用。应用线程删除了从灰色对象C到白色对象E的直接或者间接引用。 要避免对象的漏标只需要打破上述两个条件中的任何一个即可两种不同的方法核心都是采用了读写屏障 a方法一 开启写屏障当新增引用关系后触发写屏障发出引用的黑色或者白色对象会被标记成灰色例子中 A 将被标记为灰色并进入灰色集合或者将被引用对象标记为灰色。开启读屏障当检测到应用即将要访问白色对象时触发读屏障GC 会立刻访问该对象并将之标为灰色。这种方法被称为「​​​​​​​增量更新Increment Update」。 b方法二 开启写屏障。当删除引用关系前将所有即将被删除的引用关系的旧引用记录下来C - E最后以这些旧引用为根重新扫描一遍这种方法实际上是「SATBSnapshot At The Begining 算法」的一种具体实现。 注SATB 算法是由 Taiichi Yuasa 为增量式标记清除垃圾收集器开发的一个算法其核心思想是GC 开始之前会复制一份引用关系快照如果某个指针的地址被改变了那么之前的地址会被加入待标记栈中便于后面再次检查这样就可以保证在 GC 时所有的对象都会被遍历到即使指向它们的指针发生了改变。鉴于篇幅原因这里不再讲述感兴趣的读者可自行查看 Yuasa 的论文Real-time garbage collection on general-purpose machines[3]。 通过读写屏障可以解决并发标记时的漏标问题具体在工程实践中不同的垃圾回收器又有不同实现譬如针对 HotSpot 虚拟机CMS 使用了「写屏障 增量更新」的方法G1 和 Shenandoah是通过「写屏障 SATB」来完成的而 ZGC 则采取了「读屏障」的方式。 下面是 HotSpot 虚拟机中写屏障的一段代码这段代码记录下了所有的引用关系的变化情况。 void post_write_barrier(oop* field, oop val) { jbyte* card_ptr card_for(field); *card_ptr dirty_card; } 需要注意的是读/写屏障只是一种理念触发读写屏障后具体执行什么取决于垃圾回收器的实现。由于从堆读取引用是非常频繁的操作因此这两种屏障需要非常高效在常见情况下就是一些汇编代码读屏障的开销通常比写屏障大一个数量级这也是为何大多数 GC 没有使用或者很少使用读屏障的原因因为引用的读操作要远多于写操作读屏障更多的时候是用在解决并发转移时的引用更新问题上。 一些公司可能会在硬件层面对读写屏障做专门的设计便于达到最高效的垃圾回收效率。譬如Azul 公司为 Pauseless GC 算法专门定制了一整套系统包括CPU、​​​​​​​芯片组、主板和​​​​​​​操作系统用来运行具备垃圾收集功能的虚拟机定制的 CPU 内置了「读屏障指令」来实现并行的、具有碎片压缩功能的高并发(无 STW 暂停)垃圾收集算法。 注意JVM 里还有另外一组​​​​​​​内存屏障的概念读屏障Load Barrier和写屏障Store Barrier这两组指令和上面我们谈及的屏障不同Load Barrier 和 Store Barrier主要用来保证主缓存数据的一致性以及屏障两侧的指令不被重排序。 三、垃圾回收算法篇 这一部分将从最简单的垃圾回收算法开始介绍垃圾回收算法的演进情况主要介绍的算法有 标记-清除算法标记-​​​​​​​压缩算法标记-复制算法分代算法增量算法并发算法 前三种是最基础的算法后面三种是对前面三种算法在某些方面的改进。了解到上述这些算法后我们可以看到现在的很多垃圾回收器无非是是把文中提到的几种算法进行组合或取舍。如 CMS 垃圾回收器就是「标记-清除 并发算法」的组合其全称 Concurrent Mark-Sweep 也表明了这一点而 G1 是「标记-复制算法 增量算法 并发算法」的组合。 基础垃圾回收算法 基础的垃圾回收算法有标记-清除算法、标记-压缩算法和标记-复制算法这三种后面两种可以视为是对标记-清除算法中「清除」阶段的优化。 3.1 标记-清除算法Mark-Sweep 在之前介绍三色标记法时其实已经能看到标记-清除算法的影子了正是因为如此它是最简单也是最重要的一种算法。 标记-清除算法由标记阶段和清除阶段构成。标记阶段是把所有活动对象都做上标记的阶段有对象头标记和位图标记bitmap marking这两种方式后者可以与写时复制技术copy-on-write相兼容。清除阶段是把那些没有标记的对象也就是非活动对象回收的阶段回收时会把对象作为分块连接到被称为「空闲链表free-lis」的链表中去。 清除操作并不总是在标记阶段结束后就全部完成的一种「延迟清除Lazy Sweep」的算法可以缩减因清除操作导致的应用 STW 时间。延迟清除算法不是一下遍历整个堆清除所花费的时间与堆大小成正比它只在分配对象时执行必要的堆遍历同时其​​​​​​​算法复杂度只与活动对象集的大小成正比。 下图是标记-清除算法执行前后堆空间的变化情况 从上图可以看到标记-清除算法执行完成后会让堆出现碎片化这会带来两个问题 大量的​​​​​​​内存碎片会导致大对象分配时可能失败从而提前触发了另一次垃圾回收动作具有引用关系的对象可能会被分配在堆中较远的位置这会增加程序访问所需的时间即「访问的局部性Locality」较差。 上述两个问题将分别由下面介绍的标记-压缩算法和标记-复制算法来解决。 3.2 标记-压缩算法Mark-Compact 标记-压缩算法是在标记-清除算法的基础上用「压缩」取代了「清除」这个回收过程如下图所示GC 将已标记并处于活动状态的对象移动到了内存区域的起始端然后清理掉了端边界之外的内存空间。 压缩阶段需要重新安排可达对象的空间位置reloacate以及对移动后的对象引用重定向remap这两个过程都需要搜索数次堆来实现因此会增加了 GC 暂停的时间。标记-压缩算法的好处是显而易见的在进行这种压缩操作之后新对象的分配会变得非常方便——通过指针碰撞即可实现。与此同时因为 GC 总是知道可用空间的位置因此也不会带来碎片的问题。 标记-压缩算法算法还有很多变种如 Robert A. Saunders 研究出来的名为 Two-Finger 的压缩算法论文The LISP system for the Q-32 computer. In The Programming Language LISP: Its Operation and Applications[4]可以把堆搜索的次数缩短到2次 Stephen M. Blackburn 等研究出来的 ImmixGC 算法论文Cyclic reference counting with lazy mark-scan[5]结合了标记-清除和标记-压缩两种算法可以有效地解决碎片化问题。 3.3 标记-复制算法Mark-Copy 标记-复制算法与标记-压缩算法非常相似因为它们会对活动对象重新分配reloacate空间位置。两个算法区别是在标记-复制算法中reloacate 目标是一个不同的内存区域。 标记清除算法的优点很多譬如 不会发生碎片化优秀的吞吐率可实现高速分配良好的 locality 对比算法执行前后堆空间的变化可以看到不难发现标记-复制算法最大缺点在于所需空间翻倍了即堆空间的利用率很低。 标记-复制在复制阶段需要递归复制对象和它的子对象​​​​​​​递归调用带来的开销是不容忽视的。C. J. Cheney 于 1970 年研究出了迭代版本的复制算法可以抑制调用函数的额外负担和栈的消耗感兴趣的同学可以参考论文A Nonrecursive List Compacting Algorithm[6]。 垃圾回收算法的改进 下面介绍的三种垃圾回收算法会针对​​​​​​​基础算法中诸如堆碎片化、暂停时间过长、空间利用率不高等不足进行改进。 3.4 分代算法Generational GC 分代算法对基础算法的改进主要体现在该算法减小了 GC 的作用范围。如前所述标记过程和对象的 reloacate 过程都需要完全停止应用程序进行堆搜索堆空间越大进行垃圾回收所需的时间就越长如果 GC 的堆空间变小应用暂停时间也会相应地降低。 分代算法基于这样一个假说Generational Hypothesis绝大多数对象都是朝生夕灭的该假说已经在各种不同类型的编程范式或者编程语言中得到证实了。分代算法把对象分类成几代针对不同的代使用不同的 GC 算法刚生成的对象称为新生代对象对新对象执行的 GC 称为新生代 GCminor GC到达一定年龄的对象则称为老年代对象面向老年代对象的 GC 称为老年代 GCmajor GC新生代对象转为为老年代对象的情况称为晋升promotion。注代数并不是划分的越多越好虽然按照分代假说如果分代数越多最后抵达老年代的对象就越少在老年代对象上消耗的垃圾回收的时间就越少但分代数增多会带来其他的开销综合来看代数划分为 2 代或者 3 代是最好的。 在经过新生代 GC 而晋升的对象把老年代空间填满之前老年代 GC 都不会被执行。因此老年代 GC 的执行频率要比新生代 GC 低。通过使用分代垃圾回收可以改善 GC 所花费的时间吞吐量。 分代算法由于其普适性已经被大多数的垃圾回收器采用ZGC 目前不支持但也在规划中了其细节就不赘述了这里我们主要关注引入分代算法后GC 过程会出现哪些问题。 1问题1不同分代在堆空间之中如何划分 Ungar 提出的分代算法论文Generation Scavenging[7]是目前使用最多的分代划分方案该算法即为目前 CMS 垃圾回收器的原型堆空间由 eden、survivor0/survivor1、old 共四个区域组成。Ungar 的论文里新生代 GC 采用的是标记-复制算法主要是利用该算法高吞吐的特性老年代 GC 使用的是标记-清除算法因为老年代空间比整体堆要小如果使用标记-复制算法能利用的堆空间会变得更小。 分代算法的堆空间组织方式不只 Ungar 这一种方案。譬如在一些基于 Ungar 的 Generation GC 的实现中会把老年代的最后一个代通过标记-复制算法处理Lisp Machine还有的算法会把最后一个代通过标记-压缩算法回收降低复制算法出现的频繁换页的问题。 2问题2如何标记代际之间的引用关系 分代算法引入需要考虑跨代/区之间对象引用的变化情况。新生代对象不只会被根对象和新生代里的对象引用也可能被老年代对象引用GC 算法需要做到「在不回收老年代对象的同时安全地回收新生代里面的对象」新生代回收时不适合也不可能去扫描整个老年代变相搜索堆中的所有对象否则就失去了对堆空间进行分代的意义了。 解决上述引用问题的关键是引入写屏障如果一个老年代的引用指向了一个新生代的对象就会触发写屏障。写屏障执行过程的​​​​​​​伪代码如下所示其中参数 obj 的成员变量为 field该变量将要被更新为 new_obj 所指向的对象记录集 remembered_sets 被用于记录从老年代对象到新生代对象的引用新生代 GC 时将会把记录集视为 GC Roots 的一部分。 write_barrier(obj, field, new_obj){if(obj.old TRUE new_obj.young TRUE obj.remembered FALSE){remembered_sets[rs_index] objrs_indexobj.remembered TRUE}*field new_obj } 在写入屏障里首先会判断 发出引用的对象是不是老年代对象目标引用标对象是不是新生代对象发出引用的对象是否还没有加入记录集。 如果满足以上三点则本次新建的引用关系中老年代的对象会被加入到记录集。上述过程可能会带来「浮动垃圾」原因是所有由老年代-新生代的引用都会被加入记录集但老年代内对象的存活性只有在下一次老年代GC 时才知道。 分代算法的优点在于减小了 GC 的作用范围后带来的高吞吐但与此同时我们需要注意的是其假说「绝大多数对象都是朝生夕灭的」并不适用于所有程序在某些应用中对象会活得很久如果在这样的场景下使用分代算法老年代的 GC 就会很频繁反而降低了 GC 的吞吐。此外由于在记录代际引用关系时引入了写屏障这也会带来一定的性能开销。 3.5 增量算法Incremental GC 增量算法对基础算法的改进主要体现在该算法通过并发的方式降低了 STW 的时间。下图是增量算法和基础的标记-清除算法在执行时间线上的对比可以看到增量算法的核心思想是通过 GC 和应用程序交替执行的方式来控制应用程序的最大暂停时间。 增量算法的「增量」部分主要有「增量更新Incremental Update」和「增量拷贝Incremental Copying」两种前者主要是做「标记」增量后者是在做「复制」增量。 增量更新Incremental Update我们已经比较熟悉了在介绍读/写屏障的时候我们提到过由于存在并发会出现对象漏标的情况。同样的在增量算法中由于 GC 线程和应用线程是交替执行的也会出现黑色节点指向白色节点的情况增量算法中的漏标同样是通过写屏障来解决的主要有以下两种基于快照的 SATB 也可以解决增量更新时出现的漏标在此不再赘述。 1写入屏障1Dijkstra 写入屏障 write_barrier(obj, field, new_obj){if(new_obj FALSE){new_obj.mark TRUEpush(new_obj, mark_stack)}*field new_obj } 在上面的代码中如果新引用的对象 new_obj 没有被标记过会将它标记后放到 mark_stack 这个标记栈中对比三色标记法就是将这个新对象从白色对象涂成了灰色下图中的 E 2写入屏障2Steele 写入屏障 write_barrier(obj, field, new_obj){if(obj.mark TRUE new_obj FALSE){obj.mark FALSEpush(obj, mark_stack)}*field new_obj } 在上面的代码中如果新引用的对象 new_obj 没有被标记过且将要引用它的对象 obj 已经被标记过了那么会把发出引用的对象去除标记将其放入标记栈中对比三色标记法就是将发出引用的对象从黑色涂成了灰色下图中的 A。 Steele 的写入屏障相较于 Dijkstra 的写入屏障来说多了一个判断条件缺点是带来的额外的负担优点是严格的条件减少了被标记的对象的个数防止了因疏忽而造成垃圾残留的后果譬如 A 和 E 引用关系被标记后如果 E 在本轮标记过程中又称为了垃圾Dijkstra 的写入屏障还需要对 E 及其子节点进行标记而 Steele 的写入屏障就避免了这一点。 增量拷贝Incremental Copying大部分逻辑与标记-复制算法相似还是会通过遍历引用关系图把所有引用的对象拷贝到另一半堆内存不过这个过程是并发执行的。当应用程序访问到老的堆空间对象时会触发读屏障对象会从老的空间被拷贝至新的堆空间。 增量算法中大量使用了读写屏障主要是写屏障给应用程序带来了负担结果就是 GC 的吞吐相较于其他的算法来说不高。 3.6 并发算法Concurrent GC 广义上的并发算法指的是在 GC 过程中存在并发阶段的算法如 G1 中存在并发标记阶段可将其整个算法视为并发算法。 狭义上的并发垃圾回收算法是以基础的标记-复制算法为基础在各个阶段增加了并发操作实现的。与复制算法的3个阶段相对应分为并发标记mark、并发转移relocate和并发重定位remap: 1并发标记 从 GC Roots 出发使用遍历算法对对象的成员变量进行标记。同样的并发标记也需要解决标记过程中引用关系变化导致的漏标记问题这一点通过写屏障实现 2并发转移 根据并发标记后的结果生成转移集合把活跃对象转移复制到新的内存上原来的内存空间可以回收转移过程中会涉及到应用线程访问待转移对象的情况一般的解决思路是加上读屏障在完成转移任务后再访问对象 3并发重定位 对象转移后其​​​​​​​内存地址发生了变化所有指向对象老地址的指针都要修正到新的地址上这一步一般通过读屏障来实现。 并发算法是 ZGC、Shenandoah、C4 等垃圾回收器的算法基础在具体的实现中不同的垃圾回收器又有自己的选择和取舍。 至此GC 算法的​​​​​​​理论知识​​​​​​​就告一段落了有一些知识点是没有提到的如部分标记-清除算法Partial Mark Sweep的原理、保守式 GCConservative GC对数据和指针的识别、基于引用计数法的若干 GC 算法等感兴趣的同学可以参考文中列出的论文
http://www.w-s-a.com/news/862921/

相关文章:

  • 宣传式网站养生网站模板
  • 临猗网站建设天津做网站哪家服务好
  • 郑州做网站九零后用织梦建设网站的步骤
  • 莱芜网站优化加徽信xiala5江都网站制作
  • 网站开发工具书焦作网站开发公司电话
  • 石狮网站建设报价百度爱采购怎么优化排名
  • 广州网站开发系统如何建设百度网站
  • 免费建立一个个人网站网站流量图怎么做
  • 微信网站建设公司首选网站后台更新 前台不显示
  • 撰写网站专题活动策划方案未成年做网站
  • 免费在线响应式网站自助建站网页设计与网站建设试卷
  • 四川省肿瘤医院搜索优化整站优化
  • 新钥匙建站深圳创业补贴政策2023
  • 建网站需要准备什么网站三个月没排名
  • 网站运营规划网站推广的手段
  • cvm可以做网站服务器吗网片围栏
  • 培训前端网站开发网站开发 群
  • 成都武侯区网站建设wordpress菜单分类目录
  • 牡丹江市西安区建设局网站给公司做的东西放到自己网站上
  • 做网站的前景如何郑州seo规则
  • 学校户网站建设方案专业设计服务
  • 电子商务网站建设好么有一个网站怎么做cpc
  • 镇海住房和建设交通局网站跨境电商就是忽悠人的
  • 维修网站怎么做跨境电商发展现状如何
  • 手机网站设计公司皆选亿企邦桐乡市建设局官方网站
  • 企业培训 电子商务网站建设 图片山东省住房和城乡建设厅网站主页
  • 做酒招代理的网站赣icp南昌网站建设
  • 怎样做网站內链大连市建设工程信息网官网
  • 网站软件免费下载安装泰安网站建设收费标准
  • 部署iis网站校园网站设计毕业设计