03-1 50问!了解垃圾收集器必须清楚的前置知识——垃圾收集器的内存分配策略

author:陈镇坤27

创建时间:2021年12月27日01:58:45

字数:7932

文章目录

  • 03-1 50问!了解垃圾收集器必须清楚的前置知识——垃圾收集器的内存分配策略
      • 问:为什么学习垃圾收集器和内存分配策略?
      • 问:虚拟机运行时数据区域哪些模块不需要考虑垃圾收集?哪些地方又需要特别考虑呢?
    • 对象生命的算法
      • 问:怎么确定哪些对象已死?
      • 问:什么是引用计数法
      • 问:为什么Java各类主流虚拟机不使用引用计数法?
      • 问:怎么查看GC日志?怎么解读?
      • 问:什么是可达性算法?
      • 问:GC Roots具体包含什么对象?
      • 问:JDK的引用关系具体是怎么样的?
      • 问:到底引用链或者Java中垃圾回收讲的可达性分析引用链,到底一个主宾关系是什么样的?
      • 问:简单来说一个对象的死亡经历哪些阶段?
      • 问:介绍下finalize判定流程。
      • 问:为什么官方不建议使用finalize方法来让对象“重获新生”?
      • 问:方法区是不是不能回收的?
      • 问:若支持垃圾回收,方法区主要回收哪些类型的数据?
      • 问:判定方法区对应数据被废弃的条件是什么?
      • 问:怎么设置方法区回收参数?
    • 垃圾收集算法
      • 问:从判定对象消亡角度区分,垃圾收集算法分为哪几种?
      • 问:什么是分代收集理论?
      • 问:虚拟机是如何处理跨代引用对象的?
      • 问:什么是标记清除算法?
      • 问:标记清除算法有什么问题?
      • 问:什么是标记复制算法?
      • 问:标记复制算法解决了标记清除算法的什么问题?
      • 问:什么是”Appel式回收“的标记复制算法?
      • 问:当一次GC中,survivor大小不足以存放存活的对象,HotSpot虚拟机会怎么做?
      • 问:标记复制算法的优缺点。
      • 问:什么是标记整理算法?
      • 问:标记整理算法的主要作用和意义?
      • 问:介绍下标记整理算法的隐患和使用根据。
      • 问:老年代使用标记清除算法,然后参考“分区空闲分配链表”来维护碎片化的内存,是否可行?
    • HotSpot虚拟机算法实现细节
      • 问:GC Roots遍历面临什么难题,HotSpot虚拟机是如何解决的?
      • 问:垃圾回收在枚举GC Roots时,是通过什么方式得知执行上下文和全局引用的位置的?
      • 问:维护GC Roots的Oop Map需要解决什么难题?解决方案是什么?
      • 问:怎么设计安全点?
      • 问:安全点面临什么问题?怎么解决?
      • 问:什么是安全区域?
      • 问:什么是记忆集与卡表?
      • 问:记忆集是否是每一个含跨代引用的对象的集合?
      • 问:卡表精度的记忆集如何进行维护?
      • 问:为什么卡表的最简单形式是字节数组,不用bit数组呢?
      • 问:跨代引用是否只发生在老年代和年轻代之间?
      • 问:什么是写屏障?
      • 问:对卡表元素进行维护,为什么后面添加了一个可选的控制是否激活判定条件的参数?
      • 问:为什么要在一个保持一致性的快照上进行可达性分析?
      • 问:获取GC Roots时暂停所有用户线程,那整个GC Roots链可达性分析时是否也可以如此做?
      • 问:提到可达性分析的三色标记,作用是什么?
      • 问:针并发标记过程中,如何解决一致性快照的问题,有哪几种解决方案?

————————————————————————————

转载请注明出处,毕竟是呕心沥血的总结。

问:为什么学习垃圾收集器和内存分配策略?

答:Java具备自动化管理内存的能力,而当程序出现内存溢出、内存泄露等问题,异或自动化垃圾收集能力制约系统并发性能时,则需要我们对其自动化垃圾收集采取必要的管理措施。所以我们要学习这些基础知识。

此外,个人认为这能很好地帮助你理解一些"应用"层面的原理,拓展知识面,提升技术认知。

问:虚拟机运行时数据区域哪些模块不需要考虑垃圾收集?哪些地方又需要特别考虑呢?

答:程序计数器、虚拟机栈和本地方法栈,原因是这些地方随线程一同消亡,这部分区域内存的分配和回收是静态的。而堆和方法区则需要特别考虑,原因是只有在运行时,我们才知道方法需要创建的对象有多少个,大小情况如何(举个例子:查询数据库得到集合。每次执行前结果会实例化多少个对象虚拟机都不知道),因此其内存的分配和回收是动态的。

PS:初始化的栈内存大小在编译期已经确定(即时编译会有些许优化,但基于概念模型而言,我们认为编译器是确定的即可)。

对象生命的算法

问:怎么确定哪些对象已死?

答:各类计算机高级语言实现判断对象存活方法主流采取引用计数法或可达性算法。Java使用的是可达性算法。

问:什么是引用计数法

答:在对象中添加一个计数器,计算被其他地方引用的次数,次数为0则该对象死亡(没有被使用)。

问:为什么Java各类主流虚拟机不使用引用计数法?

答:因为需要解决大量例外情况。例如对象相互循环引用的问题。

问:怎么查看GC日志?怎么解读?

答:暂定(博客)

问:什么是可达性算法?

答:从GC Roots开始检索GC Roots的对象引用链(被引用关系),检索到的对象是可达的,其他对象则是不可达的。

问:GC Roots具体包含什么对象?

答:(这些对象在方法区内有类型信息)

虚拟机栈中引用的对象(这些引用包括使用到的各参数、局部变量、临时变量);

方法区中类静态属性引用的对象;

方法区中常量引用的对象;

本地方法栈中本地方法(简称JNI)引用的对象;

虚拟机内部的基本数据类型对象(对应的Class对象)和常驻异常对象、系统类加载器;

被同步锁(synchronized)持有的对象;

JMXBean(作用是反映虚拟机内部情况)、JVMTI中的注册回调、本地代码缓存。

此外,根据所选收集器的差别和当前内存区域的不同,可能会有一些临时对象加入到GC Roots中(例如跨代引用的对象)。

PS:如果只针对部分区域进行局部回收,需要注意该区域中的对象可能被其他区域中的对象所引用,因此需要去找到这些对象然后加入到GC Roots中。

问:JDK的引用关系具体是怎么样的?

答:JDK1.2以前如下

如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称该reference数据是代表某块内存、某个对象的引用

JDK1.2及以后,则将引用拓展为强引用、软引用、弱引用和虚引用。

目的是为了应对一些特殊的情况,例如一些对象在有空间不足的时候可以进行回收,其他时候不回收等。

强引用:关系存在,则不会回收对象

软引用:内存不足,关系存在,会进行回收。由SoftReference实现。

弱引用:每次GC时,关系存在,会进行回收。由WeakReference实现

虚引用:不影响引用虚引用的对象的生命周期。在对象上设置虚引用,是为了在该对象被回收时,产生一个系统通知。由PhantomReference实现。

下面是WeakReference的使用范例。

 @Dataclass User{private WeakReference a;}@Testpublic void testWeakReferenc() {WeakReference<Foo> weakReference = new WeakReference<>(new Foo());User user = new User();user.setA(weakReference);if (weakReference.get() != null) {System.out.println("GC 前非空");}System.out.println(user.getA().get());System.gc(); // 垃圾回收,释放弱引用的内存if (weakReference.get() != null) {System.out.println("GC 后 非空");} else {System.out.println("GC 后 已空");}System.out.println(user.getA().get());}

问:到底引用链或者Java中垃圾回收讲的可达性分析引用链,到底一个主宾关系是什么样的?

答:对象A的成员变量是对象B,则说B被A引用,在GC链中,表示为B-A。

引用联也就是表达一个被引用的关系链。省略了“被”字,其实不适合中文语境。

问:简单来说一个对象的死亡经历哪些阶段?

答:两个标记过程:可达性分析,若不在GC Roots引用链上,标记第一次;可达性分析结束后,判断若无实现finalize方法,或曾经执行过一次,则标记第二次。

finalize是标记第一次后的对象唯一的逃生机会。

问:介绍下finalize判定流程。

答:判断对象实现了finalize方法,将对象添加到F-Queue队列中,并在之后由虚拟机分配一条低优先度的finalizer线程来执行这些对象的finalize方法,但不保证一定执行完成(防止拖垮系统)。

收集器在最后会重新对F-Queue的对象进行GC Roots扫描,若仍然不可达,则判定死亡。

问:为什么官方不建议使用finalize方法来让对象“重获新生”?

答:其诞生是为了迎合C语言的需要,但实际可以被try-finally代替,其本身的执行无法保证顺序,不确定性大。

问:方法区是不是不能回收的?

答:不一定,要看使用的使用的虚拟机。有的是不实现、有的是部分实现。

JCP并没有规定要对方法区进行垃圾收集。ZGC便不具备方法区的类卸载功能。一个原因是方法区的垃圾收集条件苛刻。

此外,方法区的数据废弃后并不会立刻被回收,而是交由虚拟机判断。

问:若支持垃圾回收,方法区主要回收哪些类型的数据?

答:废弃的常量和不再使用的类型,包括各种字面量和符号引用。

问:判定方法区对应数据被废弃的条件是什么?

答:

常量:该字符串在系统中没有被任何字符串对象引用。

类型(必须满足三个条件):该类所有实例都被回收;加载该类的类加载器被回收;该类的Class对象没有被任何地方引用,且无法在任何地方通过反射访问该类的方法。

问:怎么设置方法区回收参数?

答: -Xnoclassgc 设置启动类型回收功能。

Product版虚拟机查看类加载和卸载信息:-verbose:class

FastDebug版虚拟机:-XX:+TraceClass-Loading、-XX: +TraceClassUnLoading

垃圾收集算法

问:从判定对象消亡角度区分,垃圾收集算法分为哪几种?

答:引用计数法和追踪式(可达性分析法)。前者被称为直接垃圾收集,后者为间接垃圾收集。

问:什么是分代收集理论?

答:一套适用于多数程序运行经验的理论法则。该理论首先以弱分代假说和强分代假说为依据,设计分代基本框架;

弱分代假说:绝大多数对象都是朝生夕死的;

强分代假说:存活越久的对象越具有生命力。

分代基本框架:将堆内存划分为不同的区域,再根据对象的年龄进行区域存储分配。

这个分而治之的基本框架具备两个优点:只需要关注少数存活的对象(整体回收代价低),集中将存活时间长的对象存放到一起(减少无必要的区域内存回收频次)。

针对不同的对象分配区域,

既产生不同的回收类型:

Partial GC(部分收集):Minor GC(年轻代回收)、Major GC(老年代回收)(只有CMS会有单独此收集);

Full GC(Java堆、方法区全收集)。

也产生不同的垃圾收集算法:标记清除算法、标记复制算法、标记整理算法。

具体对象分配区域实现上,基本可分为年轻代(Young Generation)、老年代(Old Generation)。

区域划分后,追踪式的消亡判定下,引用的对象可能不在同一个区域,因此需要解决跨区域对象引用的问题。

在前面两条法则的基础上,第三条经验假说。

跨代引用假说:跨代引用比较同代引用,仅占极少数。(同生共死)

所以,不应该为了占据少数的跨代引用对象而去遍历整个老年代。而是在新生代中维护存在跨代引用对象的关系。

PS:HotSpot虚拟机的分代式垃圾收集器的基本框架设计在后面并没有被其他收集器所遵循(技术在发展)。

PS:回收类型此外还有Mixed GC(整个新生代和部分老年代)(只有G1才有此收集)

问:虚拟机是如何处理跨代引用对象的?

答:在新生代中开辟一块空间,维护记忆集。记忆集在逻辑上划分老年代,标识对应老年代是否存在跨代引用,在新生代发生垃圾回收时,会将记忆集中标识的数据加入一起进行GC Roots扫描。

问:什么是标记清除算法?

答:标记存活对象,清除其他对象;标记死亡对象,清除死亡对象。

该算法是最基础的算法,其他算法在其身上演进而来(针对其产生的问题各自有不同的解决方案)。

问:标记清除算法有什么问题?

答:执行效率不稳定,执行时间随对象数量的增加而波动;内存碎片化,可能很快由于大对象的创建而触发gc。

问:什么是标记复制算法?

答:又称复制算法,最初(1969)的算法是“半区复制算法”,将内存划分对等的两块,运行时只使用其中一块,发生gc时,标记存活的对象,将它们复制到另一块内存,然后将本内存清空。

HotSpot虚拟机等商用虚拟机大多采用“Appel式回收”(1989)的标记复制算法。

问:标记复制算法解决了标记清除算法的什么问题?

答:内存碎片问题;清除效率问题(如果标记清除是标记死亡的话)

问:什么是”Appel式回收“的标记复制算法?

答:根据弱分代假说以及各种实验结果,设计上将新生代分为占比8的eden区和两个占比各为1的survivor区。每次复制时,将eden区和其中一个存活对象的survivor中的存活对象移动到另外一个survivor区,随后清除新生代剩余的空间。

问:当一次GC中,survivor大小不足以存放存活的对象,HotSpot虚拟机会怎么做?

答:会进行分配担保,存放到其他区域(几乎就是老年代)。

问:标记复制算法的优缺点。

答:对象的复制需要开销((弱分代假说)由于存活的对象是比较少的,所以开销也少,但又由于已经根据该现象进行分区,所以该算法产生的问题在老年代上会很明显)

存在空间浪费(Appel式回收极大减少了这个问题)

内存空间是连贯的。

问:什么是标记整理算法?

答:(1974)在标记存活对象之后,将它们移动到内存的一端,再清除移动后形成的边界以外的内存。

问:标记整理算法的主要作用和意义?

答:老年代中不适用标记复制算法。

老年代中存活对象极多,单纯的复制存活对象的方式,既需要额外大的空间分配,也可能产生对象100%存活而导致的分配担保。因此标记复制算法不适用。

问:介绍下标记整理算法的隐患和使用根据。

答:需要花费大量性能去更新大量被移动的对象的引用,该操作会“stop the word”。

老年代发生gc次数比较少,对于频繁发生内存访问的程序来说,一次gc移动虽然收集器执行效率低,但由于内存的连贯性而使得程序整体的吞吐量高(吞吐量由收集器效率及赋值器效率决定,后者占绝对控制)。

而标记清除算法比较下,虽然收集效率高,但内存碎片化可能导致存储大对象而频繁gc,单次gc的执行效率仍然保持不变,但整体程序吞吐量大大下降。

对于关注程序延迟的虚拟机来说,老年代选择标记清除算法(CMS);

对于关注程序整体吞吐量的虚拟机来说,老年代选择标记整理算法(Parallel Scavenge收集器);

PS:标记清除算法也会stop the word,但时间更短。

问:老年代使用标记清除算法,然后参考“分区空闲分配链表”来维护碎片化的内存,是否可行?

答:

不可行。内存频繁访问,而如果参考分区空闲分配链表,则在每次访问时都需要这个额外的开销,会拖垮整个程序的吞吐量。

HotSpot虚拟机算法实现细节

问:GC Roots遍历面临什么难题,HotSpot虚拟机是如何解决的?

答:GC Roots主要为虚拟机栈局部变量表的引用、方法区静态属性、常量的全局引用等。要遍历GC Roots,首先找到这些引用,如果在逐个遍历,将会非常耗费时间。

解决思路就是将查找根节点和查找引用链的过程分离。

在一个保证一致性的快照内,先一次性找到所有的GC Roots。一致性即意味着必须保证根节点对象的引用集合不发生改变,当下的方式是暂停所有用户线程(stop the word)。

问:垃圾回收在枚举GC Roots时,是通过什么方式得知执行上下文和全局引用的位置的?

答:作为准确式垃圾收集虚拟机,HotSpot用一组Oop Map维护记录所有的GC Roots。在类加载完成时,记录对象偏移量位置上的数据类型;在即时编译时,记录栈和寄存器中对应的引用。当发生垃圾收集时,直接从这组Map数据中找到并遍历GC Roots。

问:维护GC Roots的Oop Map需要解决什么难题?解决方案是什么?

答:有许多指令可能导致对象的引用关系产生变化,但不可能在每一条这种指令上都构建Oop Map(资源开销)。解决方案是在特定的位置设置安全点,只有程序执行到安全点的指令时,才会维护Oop Map(由此可见,该Map维护的引用并非就一定是该指令产生的)。

问:怎么设计安全点?

答:指令流的执行时间都非常短暂,所以不能以指令流时间长短进行设置。

安全点的执行需要有“程序长时间执行”的特征,而其中,指令序列的复用最符合该特征。因此在方法调用、循环跳转、异常跳转等复用的指令序列上产生安全点。

问:安全点面临什么问题?怎么解决?

答:如果发生垃圾收集,需要让线程跑到最近的安全点然后停下来。

为此,有两种方案设计:

抢先式中断:发生垃圾收集时,中断所有用户线程,未在安全点的线程激活继续执行,再中断并判断是否在安全点,反复执行直到满足临界条件(在安全点)。(几乎没有虚拟机采用)

主动式中断:设置一个标志位,所有线程在执行时,轮循该标志位。轮循位置一般和安全点重合,在创建对象等的分配内存的区域也会触发。

HotSpot虚拟机采取内存保护陷阱方式,标志位是一个内存页,访问时若不可读,则产生一个自陷信号,并由预先注册的异常处理器挂起线程。

问:什么是安全区域?

答:对于一些"不执行"(例如睡眠状态)的线程,由于无法响应虚拟机的中断请求,虚拟机也不可能等待该“不执行”状态结束,为了保证根节点枚举的执行,引入安全区域概念。

安全区域内,对象的引用关系不会发生变化。线程进入安全区域时,会标记自己进入了安全区域。垃圾回收时,不会要求这些在安全区域的线程进入中断。若线程“不执行”状态结束,线程恢复执行,要离开安全区域时,会检查虚拟机是否正在开启了一致性快照。若是,则一直等待,直到收到可以离开的信号。若否,则继续执行。

问:什么是记忆集与卡表?

答:记忆集记录在垃圾收集的目标区域中,内容为非收集区域指向收集区域的引用集合的抽象数据结构。(注意:抽象)

抽象的数据结构,则具体的实现由虚拟机决定。

问:记忆集是否是每一个含跨代引用的对象的集合?

答:不是。它是一种抽象数据模型(只定义行为意图),可拥有多种具体实现。若简单使用对象数组组成该集合,会造成非常大的性能开销:维护;空间开销——内存占用。

将维护的数据模型的记录粒度放粗犷一些,以节省资源开销。

一般根据收集的粒度,将记忆集的抽象数据结构的具体实现主要有:字长精度、对象精度、卡精度。

字长精度:每个记录精确到一个机器字长。字长包含跨代指针。

对象精度:每个记录精确到一个对象,对象内含有跨代指针。

卡精度(最常用,甚至被视为记忆集等价符号):每个记录精确到一个内存区域,该区域内有对象含有跨代指针。

问:卡表精度的记忆集如何进行维护?

HotSpot虚拟机使用的是卡精度记忆集。

卡表最简单的形式可以是一个字节数组,而HotSpot虚拟机使用的正是一个字节数组。

标记逻辑如下:

为什么要向右位移9位呢?位移9位,即除以2的9次方。

其实这里的address是对象的地址,如果确定一个对象存在跨代引用,则拿该对象的地址除以512(每个卡页固定设计是512。是2的9次幂),可以知道对象理应在card_table数组的第几个元素上,然后对元素的值进行标识(1表示存在(变脏),0表示不存在),即可表示该对象的所在内存区域存在跨代引用对象。

通过获取卡表字节数组中值为1的元素对应的序号,可以很快计算出对应内存地址范围存在跨代指针(跨代对象)。

问:为什么卡表的最简单形式是字节数组,不用bit数组呢?

答:因为字节是计算机硬件寻址的最小单位, 用bit反而需要一些指令开销,比较之下,多一点内存占用无碍。

问:跨代引用是否只发生在老年代和年轻代之间?

答:所有的涉及部分收集的垃圾收集器,都会面临跨代引用问题。如G1、ZGC、Shenandoah。

问:什么是写屏障?

答:记忆集的作用使得获取GC Roots时,不需要去扫描整个老年代,但同时产生一个问题——如何维护记忆集?

记忆集本质上,是对new出的对象是否包含跨代引用的内存精度记忆。

所以,记忆集最恰当的维护时机是在对象进行内存赋值的时刻。

在虚拟机中,解释执行处理的是字节码指令流,虚拟机可以充分介入,但即时编译是提前将字节码编译为纯粹的机器指令流。

因此,需要在机器码层面对对象的内存赋值进行卡表的维护。

写屏障作用,相当于一个aop切面,在虚拟机执行赋值时,进行环形切面,生成对应机器指令。

如果是在赋值前进行操作,成为写前屏障,赋值后则为写后屏障。G1之前,都是写后屏障。

在早前,写屏障中,若对卡表进行“变脏”的更新,则直接对卡表字节数组进行赋值即可。

在JDK7之后,通过参数

-XX:+UseCondCardMark

可启动赋值判断:若卡页已脏,则不进行赋值。

问:对卡表元素进行维护,为什么后面添加了一个可选的控制是否激活判定条件的参数?

答:对卡表进行维护,会涉及到操作系统层面的“伪共享”问题。

中央处理器的缓存系统以缓存行 作为存储单位。若多线程操作的目标变量同处一个缓存行,将会导致操作的性能降低。

若缓存行为64byte,而卡表字节数组每个元素占1byte,则对一个连续的32kb内存中的对象的赋值,所关联影响的64个连续的卡表字节元素所映射的缓存行为同一行时,机器的操作性能会大大降低。

因此后面增加了一个可控的判断是否进行赋值的操作,但这个判定条件同样需要性能开销。

问:为什么要在一个保持一致性的快照上进行可达性分析?

答:在进行可达性分析时,若无法保证逻辑意义上的一致性快照。则当同时满足以下两个条件时,说一个本应存活的对象被标记消亡了。这是不允许接收的。

1、GC Roots链扫描指针对象的后续暂未扫描对象集合中,有存活对象解除了GC Roots链上的引用;

2、该存活对象与已扫描指针对象建立引用关系。

这时,由于可达性分析是从GC Roots向下扫描的,该存活对象因为没扫描到,所以被判定死亡。

所以必须保证在扫描时,实现一个逻辑意义上的一致性快照。

问:获取GC Roots时暂停所有用户线程,那整个GC Roots链可达性分析时是否也可以如此做?

答:不行。GC Roots作为根节点,与整个可达性分析所扫描的对象总和数量比较相对稀少。此外还有安全点、记忆集等方式对其进行性能的维护。而整个GC Roots的暂停用户线程,是不可接受的。

问:提到可达性分析的三色标记,作用是什么?

答:帮助我们理解可达性分析的过程。

黑色对象是从GC Roots开始被扫描过的对象,灰色对象是正在被扫描的,尚且还有引用未被扫描的对象,白色对象是尚未扫描的对象。

整个可达性分析的过程,就如同在沙滩蔓延的浪花,是呈波浪推进的过程。波浪之后是存活对象,波峰是正被扫描的对象,而其他没有被波浪覆盖到的,都是未扫描对象。

问:针并发标记过程中,如何解决一致性快照的问题,有哪几种解决方案?

答:GC Roots的遍历,除了根节点需要短暂暂停用户线程外,其他过程是可以进行并发标记优化的。但并发标记下,也同时存在对象的引用关系变化而产生的一些问题,诸如存活对象丢失、死亡对象未能清除等。

其中,尤其存活对象丢失是不能接受的,为了解决这个问题,需要破坏产生这个问题的要素。

解决方案是在并发标记过程中,GC Roots链上的对象关系发生变化时,会进行对应的记录。根据记录方式的不同,将解决方案归纳为以下两种:

方案一:增量更新;黑色对象插入新的白色对象时,记录黑色对象,待并发扫描结束时,对这些引用关系发生变化的对象进行第二次扫描。

方案二:原始快照。
PS:下面是我曾经错误的理解

原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删 除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描 一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来 进行搜索
我们注意原文是如何描述的:在并发扫描结束后,重新以灰色对象为根重新扫描。
这句话本身,是存在疑点的。按照可达性分析的描述,GC Roots链是波纹式【向下】推进的,即使在白色对象发生引用关系解绑时,记录白色对象的灰色对象,那第二次扫描,若白色对象重新建立的连接是黑色对象,此时白色对象依然会发生丢失。
实际上理解的关键点就在于原始快照的应用虚拟机,是G1.
G1的记忆集,不是传统的仅仅年轻代记忆老年代,而是region之间的记忆。所以,原始快照应该是记录白色对象,然后在并发扫描结束后,再通过记忆集找出是否存在被黑色对象的引用,若存在,可能还会从该白色对象开始第二次的可达性分析。

最新的理解
原始快照和增量更新都会利用写屏障,进行引用关系变化的记录。

但它们两者的利用方式不同,从下面的解读,你就可以比较清晰地了解为什么一者称“增量更新”,一者称“原始快照”。

CMS采用的是写后屏障,黑色对象新增了白色对象,会在新增后去记录黑色对象到白色对象的引用。

G1采用的是写前屏障,在灰色对象断开白色对象前,先记录灰色对象到白色对象的引用。第一个标记阶段结束后,再对此前标记时,写前屏障记录的引用进行二次扫描,从而产生了一种“快照”的效果。当然,这种快照效果可能产生一些已经死亡的对象被标记存活的情况,但这是可以接受的。

03-1 手敲八千字,认识垃圾收集器必须清楚的前置知识【垃圾收集器的内存分配策略】相关推荐

  1. Pandas教程【国宝级教程,一万八千字总结】

    Pandas教程[国宝级教程,一万八千字总结]

  2. 论文超详细精读|八千字:AS-GCN

    文章目录 前言 总览 一.Introduction 背景 AS-GCN 主要贡献 二.Related Works 三.Background 3.1 Notations (符号注记) 3.2 Spati ...

  3. 八千字硬核长文梳理Linux内核概念及学习路线

    点击上方"大鱼机器人",选择"置顶/星标公众号" 福利干货,第一时间送达! 来源 :头条号@Linux学习教程,冰凌块儿 整理:公众号:嵌入式Linux,发哥 ...

  4. 八千字,带你看示波器的发展史。

    01  史前时代 电子示波器的起点并不容易查证,所以史前时代由示波器的操作特性来划分.如今我们最常使用的可能是边沿触发模式,甚至通常认为这就是示波器的一部分基本功能.实际上在TEK 511之前,示波器 ...

  5. esc指令检查打印状态_【行业知识分享】八千字解读ESC系统

    汽车电子稳定控制系统ESC(Electronic StabilityController)是一个主动安全控制系统,通过传感器监控车辆自身行驶状态,在车辆紧急躲避障碍物.转弯等容易出现不稳定状况时,以及 ...

  6. 八千字长文深度解读,迁移学习在强化学习中的应用及最新进展

    点击我爱计算机视觉标星,更快获取CVML新技术 本文经机器之心(微信公众号:almosthuman2014)授权转载,禁止二次转载. 机器之心原创 作者:Luo Sainan 编辑:H4O 迁移学习通 ...

  7. 百家争鸣前夕,八千字说透「波卡平行链插槽拍卖」

    在 Web3 基金会的长期支持,生态内项目方的协作耕耘,资本机构的布局投入之下,波卡的未来定会欣欣向荣. 编者按:本文来自36氪战略合作区块链媒体"Odaily星球日报"(公众号I ...

  8. 《数据结构》(一)时间复杂度和空间复杂度(超硬核八千字)

    ------------------时间复杂度和空间复杂度--------------------- 1.时间复杂度和空间复杂度 在学习数据结构和算法之前,每一个初学者第一步要熟练掌握的就是时间复杂度 ...

  9. react 八千字长文深入了解react合成事件底层原理,原生事件中阻止冒泡是否会阻塞合成事件?

    壹 ❀ 引 在前面两篇文章中,我们花了较大的篇幅介绍react的setState方法,在介绍setState同步异步时提到,在react合成事件中react对于this.state更新都是异步,但在原 ...

最新文章

  1. npoi的mvc怎么ajax导出,asp.net mvc利用NPOI导入导出Excel解决方法
  2. 常考数据结构与算法:异或操作
  3. 匿名电子病例信息不全?深度学习能更好的判断出种族(代码开源)
  4. “许巍日”新歌提前曝光 《爱如少年》10/15温暖登场!
  5. InnoDB引擎与MyIASM的一点总结
  6. php面试题接口方面,php面试题6 - osc_xb4v1nhl的个人空间 - OSCHINA - 中文开源技术交流社区...
  7. MVN TEST指定运行脚本
  8. 【2019徐州网络赛:M】Longest subsequence(思维+构造)
  9. html 中添加提示,如何interpretHTML UIB-提示
  10. 为Oracle 18c数据库打PSU补丁
  11. 立创开源丨无刷电机驱动FOC驱动板
  12. Qt/C++ 加载数据库出现qt_sql_default_connection提醒
  13. 不同计算机用户的区别是什么意思,电脑操作系统的32位和64位分别是什么意思?有什么区别?...
  14. MyBatis01:第一个程序
  15. echart实现中国地图
  16. 给查询出的SQL语句加序号
  17. 富文本编辑vue插件vue-quill-editor
  18. 2022(秋)工程伦理答案 第二章
  19. 计算机日常应用之教学PPT-1
  20. Behaviors should be constructed with Behavior()

热门文章

  1. 培训机构和在线教程还能不能信任?
  2. wget命令 scp命令 rcp命令
  3. Java各类技能知识点学习链接大全:七、Linux命令
  4. 【Nginx】第二十二节 redirect跟permanent区别
  5. 例题SQL语句详解-数据库基本操作11-排序分组联合
  6. 1:输出1-100之内的所有质数
  7. 索尼 LT26I刷机包 X.I.D 加入官方风格 GF A3.9.4 各方面完美
  8. XPS如何在线批量转换成PDF
  9. (附源码)node.js游云旅游网站 毕业设计231547
  10. 【五六七人口普查】我国省市两级人口基本情况