1.java堆的基本结构与一个对象在堆中的分配


其中的Eden,Survivor0和Survivor1被称为新生代,Tenured被称为老年代。
当一个对象被new出来之后,如果他非常的大,那么直接装入Tenured中。否则装入Eden中,在一次GC之后,Eden中存活下来的对象就复制到Survivor0中。接下来的GC中如果Survivor0中存活下来的对象就复制到Survivor1中,接下来存活的对象就在Survivor0和Survivor1中不断移动,如果经过比较多次的GC之后Survivor0和Survivor1中还有对象存在,则将他移动到Tenured中。
Survivor0和Survivor1使用的是标记-复制算法,Tenured中使用的是标记-整理算法。

2.如何判断对象已死?

引用计数算法:
我们可以为每一个对象设置一个count引用计数器,每当一个地方引用它的时候,计数器就加1,当引用失效的时候,计数器就减1;当计数器的值为0的时候,就代表着这个对象不会再被使用了,这个时候就可以对该对象进行回收。
这个算法十分简单,但是并没有被广泛的使用。因为这个看似简单的原理需要考虑很多例外的情况。
可达性算法:
这是当前比较主流的判断对象是否应该被回收的方式。基本思路就是通过一系列被称为“GC Roots”的跟对象作为起始节点集,如果某个对象到GC Roots之间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象已经不可达的时候,则证明这个对象不可能再被使用了。

由上图可以出Object A,Object B,Object C和Object D对于GC Roots都是可达的,因此表示这几个对象还存在对于他们的引用,但是Object E和Object F对于GC Roots都是不可达的,则他们就应该被回收。这个时候就出现了一个问题,什么样的东西可以被看作GC Roots?
可以作为GC Roots的对象:
(1)在虚拟机栈(本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用的参数、局部变量、临时变量等。
(2)在方法区中类静态属性引用的对象,譬如java类的引用类型静态变量。
(3)在方法区中常量引用的对象。
(4)在本地方法栈中的Native方法引用的对象。
(5)java虚拟机内部的引用。
(6)所有被同步锁(synchronized关键字)持有的对象。
(7)反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调,本地代码缓存等。

3.关于引用

jdk1.2之后,java对引用的概念进行了扩充,将引用分为强引用(Strongly Reference),软引用(Soft Reference),弱引用(Weak Reference)和虚引用(Phantom Reference)。这四种引用的强度依次减弱。
强引用(Strongly Reference):就是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj = new Object()”这种引用关系。无论何时,只要强引用存在,垃圾收集器就永远不会回收被引用的对象。
软引用(Soft Reference):用来描述一些还有用,但非必须的对象。只要有被弱引用的对象,在系统将要发出内存溢出异常之前,会把这些对象列入回收范围之中进行第二次回收,如果这次回收之后还没有足够的内存,才会抛出内存溢出异常。
弱引用(Weak Reference):与软引用的定义类似,但比软引用更弱,被弱引用的对象只能生存到下一次垃圾收集为止,当垃圾收集器开始工作时,无论内存是否足够,都会回收掉被弱引用的对象。
虚引用(Phantom Reference):也称“幽灵引用”和“幻影引用”。无法通过虚引用来取得一个对象的实例。它存在的唯一目的是为了能在这个对象被垃圾收集器收集的时候收到一个系统的通知。

即使在可达性算法中被判定为不可达的对象,是否就一定救“非死不可”呢?
一个对象在被垃圾收集器回收的过程中总需要经历两个阶段。第一个阶段就是被判定为不可达的状态,这时候就进入第二个阶段,这个阶段会对对象进行筛选,即是否有必要执行finalize()方法进行回收。如果该对象没有覆盖finalize()或者已经调用过finalize()方法,则被判定为没有必要执行。如果被判定为有必要执行,则虚拟机会将该对象放置在一个F-Queue的队列中,并在稍后由一条由虚拟机自动建立的,低优先级的Finalizer线程去执行finalize()方法。但是虚拟机只负责开始这个方法,不负责他完整的运行完,这是因为如果某个对象的finalize()方法执行缓慢或者发生其他错误,则就会影响队列中的其他对象。finalize()方法是对象逃脱死亡的最后一个机会,稍后收集器将对F-Queue中的对象进行第二次标记,如果对象在finalize()中成功拯救自己——即重新与任意对象建立关系链,则可以避免被回收。

方法区的垃圾回收效率要低于堆区的垃圾回收效率。

4.垃圾收集算法

当前的大多是商用虚拟机的垃圾收集器,都遵循“分代收集”的理论。主要是建立在两个假说之上:
(1)弱分代假说:绝大多数对象都是朝生夕灭的。
(2)强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
即虚拟机应该划分出多个不同的区域,然后将回收对象根据年龄分配到不同的区域之中,对他们采取不同的使用回收策略。

在现在的虚拟机中,设计者至少会把堆划分为新生代(Young Generation)和老年代(Old Generation)两个区域,顾名思义,新生代区域中在每一次垃圾回收的过程中都会有大量的对象死去,存活的少量对象将会逐步晋升到老年代中进行存放。

标记-清除算法:
这是最早出现的垃圾收集算法。算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后再统一回收点所有被标记的对象。
这个算法存在两个主要缺陷:
1.对一个对象进行标记和清除两个阶段的操作会随着对象数量的增多而变得低效。
2.该算法回收之后的区域会变得碎片化,空间难以再次利用。

标记-复制算法:
简称为复制算法。主要是为了解决标记-清除算法在对象数量较多时执行效率慢的问题。
它将内存分为大小相同的两个部分,每次分配的时候只在其中一个部分进行分配,当这个部分满了之后,就执行垃圾回收过程,并将在垃圾回收中存活的内存块复制到另一个部分保留下来,然后清空第一个部分用来下一轮的分配。一般经过垃圾回收之后存活的内存块的数量比被回收的数量少得多,因此复制的开销并不会很大。而且也解决了碎片化的问题,每次分配只需移动第一个部分的指针,并且清楚也是将第一个部分全部清除,不存在部分清楚部分保留的碎片化情况。但是这个算法也存在着响应的缺陷,就是可利用的么内存只剩下原来的一半了。
现在的大部分虚拟机都优先使用这种算法,但是内存的分配并不是严格的按照1:1进行区分,因为绝大部分的对象都无法在第一轮垃圾回收之后存活。

标记-整理算法:
标记-复制算法如果在存活的对象比较多的时候就显得不是那么合适了,因为需要进行大量的复制工作。
标记-整理算法其实前面的步骤与标记-清除算法是一样的,不过它并不是简单的对对象进行回收,而是先把存活的对象集中移动到内存的一段,然后再对回收对象进行回收。他解决了内存碎片化的问题,但是又引入了一个新的问题,移动后再进行回收真的是百利无害的操作吗?
显然他的优点是规避了内存碎片化的问题,但是当在像老年代区域中有大量对象存活的情况下,移动就是一项十分费时费力的操作了,因为在移动的过程中就必须全程暂停用户应用程序,即“Stop The World”。
可以简单的理解为,如果移动,内存回收就会显得更复杂;如果不移动,内存分配就会显得更复杂。

5.HotSpot的算法细节实现

根节点的枚举:
无论是效率多高的虚拟机,在进行根节点枚举(查找GC Roots)的时候,都必须暂停用户线程,即和整理内存碎片一样需要“Stop The World”。但是因为现在的虚拟机都是使用的准确式垃圾收集,HotSpot中使用一组成为OopMap的数据结构来存储每个对象的信息,这样收集器在扫描的时候就可以直接得到这些信息,而不需要在GC RootsSet中一个一个的遍历查找。

安全点:
一个线程并不是在任意时候都可以停止下来进行垃圾回收过程的,因为这样会使得整个系统的开销变得很大而且线程的执行比较混乱。因此我们在每个线程中设置安全点,只有当执行到安全点的时候才允许进行垃圾回收过程。那么怎么让系统中的所有线程都达到安全点以进行垃圾回收呢?有抢占式中断和主动式中断两种方式。
抢占式中断(Preemptive Suspension):系统在垃圾回收发生时,先中断所有线程,如果发现还有线程没有到达安全点,就恢复这条线程继续执行。
主动式中断(Voluntary Suspension):当系统需要垃圾回收时,不直接对线程进行操作,而是为每个线程设置一个标志位,各个线程在执行过程中不断去轮询这个标志位,一旦发现中断标志为真的时候,就自己在最近的安全点上挂起。

安全区域:
安全点保证了线程执行过程中不必等待太久就会遇到可以进入垃圾收集过程的安全点。但是如果程序“不执行”呢?例如线程处于“Block”或者“Sleep”状态。因此就引入了安全区域进行解决。
安全区域是指能够确保某一段代码片段之中,引用关系不会发生变化,因此在这个区域之中任意地方开始垃圾收集都是安全的。也可以看做被拉长了的安全点。
当线程要进入安全区域的时候,就会像虚拟机报告,那么之后虚拟机在需要执行垃圾回收的一系列操作的时候就不需要管这些处在安全区域的线程;当线程要离开安全区域的时候,需要先检测虚拟机是否已经完成了根节点枚举(或其他需要暂停所有用户线程的操作),如果完成了,就直接走出安全区域即可,否则就要一直等待知道虚拟机完成这些操作。

并发的可达性分析:
由于各种技术的加持(Oop Map),对于GC Roots对象的枚举已经不再是制约垃圾回收过程效率的瓶颈了,相对的,随着对象的逐渐增多,从GC Roots延伸出去的对象的数量与复杂度不断地增加,成为了影响停顿时间的主要因素。
引入三色图来分析一下这一过程:
a.白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始所有的对象都是白色的;若在分析结束阶段,仍然是白色的对象,就是不可达的对象。
b.黑色:表示对象已经被垃圾收集器访问过了,且这个对象的所有引用都已经扫描过了。黑色的对象代表扫描过的,安全存活的,如果有其他对象的引用指向黑色的对象,则无需对他进行扫描,黑色对象不可能直接指向白色的对象。
c.灰色:表示对象已经被垃圾收集器访问过,但是还存在至少一个引用还没有被扫描过。

研究发现,当同时满足一下两个条件的时候,就会产生“对象消失”的问题,即原本应该是黑色的对象被误标成白色:
a.赋值器插入了一条或多条从黑色对象到白色对象的新引用;
b.赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。
因此我们只需要破坏两个条件中的任意一个就可以解决“对象消失”的问题。由此产生了两种解决的方案,增量更新和原始快照。
增量更新破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后再将这些记录过的引用关系中的黑色对象为根,重新扫描一遍。可以简单的理解为,黑色对象一旦新插入了指向白色对象的引用之后,他就变回黑色对象了。
原始快照破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束的时候,再将这些记录过的引用关系中的灰色对象为根,重新扫描一遍。可以简单地理解为,无论引用关系删除与否,都会按照刚刚开始的那一刻的对象图快照进行搜索。

6.经典垃圾收集器

Serial收集器
Serial收集器是历史最悠久的收集器,他是一个单线程工作的新生代收集器,这里的单线程指的是它在进行垃圾收集的时候必须暂停其他工作进程,直到收集结束。但是这种“Stop The
World ”的体验是极度不友好的。于是,从Serial收集器开始,收集器的设计都遵循着减少用户线程的停顿时间这一基本的目的进行设计,但是仍然无法根本消除这一停顿。
Serial收集器的优点就是占用的资源是最少的,比较简单高效,因为它没有太多的线程交互的开销,垃圾收集过程简单纯粹。

ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,同时使用多条线程进行垃圾收集,其他的行为与Serial收集器可用的所有控制参数,收集算法,Stop The World等都完全一致。ParNew收集器的另外一个存在的条件就是它是除了Serial收集器外唯一能与CMS收集器一起工作的收集器,这(在G1收集器出现之前)是一种十分流行的垃圾回收搭配方案。

Parallel Scavenge收集器
Parallel Scavenge收集器是一款基于标记-复制算法的新生代收集器,与ParNew收集器十分类似。Parallel Scavenge收集器的特点是他关注的点与其他的收集器不同,像CMS等收集器关注的是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是处理器用于运行用户代码时的事件与处理器总消耗时间的比值。即Parallel Scavenge收集器关注的是处理器用于垃圾回收的时间的长短控制。

Serial Old收集器与Parallel Old收集器
分别是Serial收集器与Parallel Scavenge收集器针对老年代的版本。

CMS收集器
CMS收集器(Concurrent Mark Sweep)是基于标记-清除算法实现的,运行过程主要分为以下四个步骤:
a.初始标记(CMS initial mark)
b.并发标记(CMS concurrent mark)
c.重新标记(CMS remark)
d.并发清除(CMS concurrent sweep)
其中的初始标记和重新标记都需要Stop The World。初始标记首先遍历与GC Roots直接相连的对象,速度很快。并发标记就是从GC Roots直接相连的对象开始遍历整个图的过程,这个过程耗时长但是不需要停顿其他用户进程。重新标记则是为了修正在并发标记过程中因为用户操作而产生变动的一些情况。最后并发清除的过程就是清除掉之前标记的对象,因为不需要进行对象的移动,此过程也可以与其他的用户线程一起运行。
可以发现,在最消耗时间的并发标记和并发清除两个环节当中都不需要暂停其他用户线程,这就使得CMS收集器在处理用户停顿时间这一问题上有了一次很大的飞跃。
但是CMS收集器也有以下三个缺点:
a.对处理器资源非常敏感。其实所有的并发式收集器都对处理器资源比较敏感,在并发阶段,虽然不会导致用户线程暂停,但是却因为占用了一部分的线程而导致应用程序变慢,降低总吞吐量。为了缓解这种情况的发生,虚拟机提供了一种称为“增量式并发收集器”的CMS收集器变种,它使得用户线程和收集器线程交替运行,减少收集器线程单独占据处理器的时间。但是这种模式因为体验不佳已经在JDK 9被完全废弃。
b.CMS无法处理“浮动垃圾”。因为CMS的并发标记是与用户线程一起运行的,在这段时间内产生的新的垃圾并没有办法被标记到,只能等到下一次垃圾收集过程中进行处理,这部分垃圾称为“浮动垃圾”。
c.因标记-清除算法带来的内存碎片化的问题。

Garbage First收集器
G1收集器的一个特点就是他不再局限于在老年代收集,在新生代收集或是在整个java堆中收集这一模式,而是面向堆内任何部分组成回收集进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收效益最大,这就是G1收集器的Mixed GC模式。G1收集器不再将堆机械得分为老年代,新生代这种整块的区域,而是分为一个一个的Region(区域),然后会通过对各个Region回收价值的分析排序,以及用户所期望的停顿时间对Region进行有计划的回收。
G1收集器的运作过程也可以分为四个步骤:
a.初始标记:仅仅只是标记一下GC Roots能直接关联到的对象。需要暂停用户线程。
b.并发标记:从GC Roots开始对堆中的对象进行可达性分析,递归扫描整个图,找到要回收的对象。可与用户线程并发执行。
c.最终标记:对用户线程进行暂停,用于处理并发标记遗留下来的最后少量的SATB记录。
d.筛选回收:负责更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多的Region构成回收集,然后把存活的Region复制到空的Region中,在清理掉旧Region的全部空间。这里涉及到存活对象的移动,也需要暂停用户线程。
从上述过程可以看出,G1只有并发标记不需要暂停用户线程,因此G1并不只是单纯的追求低延迟,而是想成为一个在延迟可控的情况下将获得尽可能高的吞吐量的“全功能收集器”。
相比于CMS收集器,G1收集器的最大的优点就是可以指定最大停顿时间,分Region的内存布局,按受益确定回收集这些创造性设计带来的红利。但是G1收集器无论是垃圾收集产生的内存占用还是程序运行时的额外执行负载都比CMS要高。

7.低延迟垃圾收集器

因为之前的垃圾收集器无论是CMS还是G1,都还是存在着一定程度上的“Stop The World”的现象,而这个停顿时间的问题日益成为限制收集器性能瓶颈,因此在后来的发展中出现了两款几乎整个处理过程都是并发进行的垃圾收集器。
Shenandoah收集器
Shenandoah收集器是一款只存在于OpenJDK中而不存在于OracleJDK的垃圾收集器。
Shenandoah收集器相对于G1和CMS最主要的改进之处就是他可以并发的进行垃圾收集之后的整理工作,这就大大的减少了停顿时间的产生。
其实Shenandoah收集器在绝大部分的工作原理上与G1十分相似,但是也至少存在着是三个不同。首先就是回收垃圾之后的整理过程可并发,G1的清除过程可以并发,但是整理过程还是需要暂停用户线程;第二就是Shenandoah没有分代的概念,所以在权衡清除价值的时候,与对象处于什么世代无关;最后就是Shenandoah摒弃了G1中耗费大量内存和资源去维护的记忆集,改用“连接矩阵”的全局数据结构来记录Region的引用关系。“连接矩阵”就是一个二维表,记录对象之间的连接关系。
Shenandoah收集器的工作过程可以分为以下九个阶段:
(1)初始标记:与G1一样,标记直接与GC Roots相连的对象,需要暂停用户线程;
(2)并发标记:遍历从GC Roots出发的所有对象的可达性,可与用户线程并发执行;
(3)最终标记:与G1一样,处理剩余的SATB扫描,并统计出回收价值最高的Region,在统计阶段也会造成用户线程的暂停;
(4)并发清理:这个阶段用于清理那些连一个存活对象都没有找到的Region;
(5)并发回收:这个阶段是Shenandoah收集器与之前的所有收集器有所区别的阶段。Shenandoah需要把回收集中的存活对象先复制一份到其他未被使用的Region中(标记复制算法)。复制这一操作如果在用户线程暂停的情况下进行就显得十分简单,但是如果要求与用户线程并行,就显得十分复杂了。Shenandoah会通过读屏障和被称为“Brooks Pointers”的转发指针来解决这一困难。
(6)初始引用更新:并发回收阶段复制结束之后,还需要把堆中所有指向旧对象的引用修正到复制后的新地址,这个操作成为引用更新。这个过程会造成一个短暂的用户线程暂停。
(7)并发引用更新:这是真正进行引用更新的过程,这个阶段与用户线程并发进行。它与并发标记不同,不需要再遍历整个图,只需要按照内存物理地址的顺序,线性搜索出引用类型,把旧值修改为新值即可。
(8)最终引用更新:解决了堆中的引用更新之后,还要修正存在于GC Roots中的引用,这个阶段是Shenandoah的最后一次用户线程暂停过程,停顿时间只与GC Roots的数量有关。
(9)并发清理:经过并发回收和引用更新后,整个回收集中所有的Region已再无存活对象,因此再调用一次并发清理过程来回收这些Region空间。

在这一系列的过程中,Shenandoah的核心就是如何在复制的过程中实现与用户线程并发。那就是利用了Brooks Pointers(转发指针)技术。这种技术就是在原有的对象布局前面加入了一个引用字段,在正常不处于并发移动的情况下,该引用指向对象自己;而并发复制所面临的问题就是:移动是一瞬间的问题,但是移动之后整个内存所有指向该对象的引用都还是指向旧地址,这是很难一瞬间改变的,而在这个过程中用户线程又会对被移动的对象进行读写访问,这就造成了前后信息不一致的问题。Brooks Pointer的出现,使得在完成内存区域的复制移动之后,只需要通过修改转发指针,将指针指向对象存在的新地址上,这样虚拟机内存中所有通过旧引用地址访问的代码便仍可使用,都会被自动转发到新对象上继续工作。虽然大大缩短了更改地址的操作耗时,但是如果在虚拟机更改转发指针之前有一个用户线程访问了对象,那么它仍然会操作到旧对象上,造成错误,因此依然需要对这个阶段采取同步操作的限制。

ZGC收集器
我们先用一句话来概括ZGC的主要特征:ZGC是一款基于Region内存布局的,不设分代的,使用读屏障,染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。
ZGC的Region与G1和Shenandoah大致相同,但是ZGC的Region是动态的,即动态创建和销毁以及动态的区域容量大小。
Shenandoah收集器使用Brooks Pointers和读屏障来解决并发整理的问题,而ZGC则使用染色指针技术(Colored Pointer)。
染色指针其实就是将之前一般存储在对象之中的一些对象相关的信息改变为存储在对象指向该引用的指针中,这样使得即使引用存在被移动的可能,虚拟机也可以通过对象指向引用的指针直接的对引用的一些信息进行访问。
ZGC的可分为以下工作阶段:
(1)初始标记:标记直接与GC Roots相连的对象;
(2)并发标记:遍历整个图进行标记;
(3)最终标记:完善标记的结果;
(4)并发预备重分配:根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集。
(5)并发重分配:这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表,记录从旧对象到新对象的转向关系。得益于染色指针的支持,这一过程能仅仅通过指针实现与用户线程的并发,这一行为称为指针的“自愈”能力。
(6)并发重映射:修正堆中指向重分配集合中旧对象的所有引用。

JVM(2):垃圾收集器与gc相关推荐

  1. JVM面试(四)-垃圾回收、垃圾收集器、GC日志

    垃圾回收.垃圾收集器.GC日志 什么是垃圾?(垃圾的概念) 什么是垃圾回收?(垃圾回收的概念) 为什么要垃圾回收?(垃圾回收的原因) 如何定义垃圾? 引用计数算法 什么是循环引用 可达性分析算法 哪些 ...

  2. JVM之垃圾收集器回收种类

    JVM之垃圾收集器回收种类 目录 面试常见问题 串行并行并发G1四大垃圾回收方式 如何查看默认的垃圾收集器 JVM默认的垃圾收集器有哪些 GC之7大垃圾收集器详解 1. 面试常见问题 GC垃圾回收算法 ...

  3. gc垃圾收集器 与gc算法_GC解释:收集器概述

    gc垃圾收集器 与gc算法 当前版本的HotSpot JVM包括三种类型的垃圾收集器: –串行收集器 –并行收集器 –多数同时收集者 它们都是世代的,这意味着它们利用了堆的划分方式 . 垃圾收集器负责 ...

  4. JVM常用垃圾收集器

    前言 在上一篇,我们谈到了JVM中的常用垃圾回收算法,并了解了JVM中针对堆区中不同的分代采用不同的垃圾回收算法 在了解了垃圾回收算法之后,很多伙伴不禁在想,既然是分代垃圾回收,自然新生代和老年代的垃 ...

  5. JVM(四)--垃圾收集器

    JVM(四)–垃圾收集器 这篇博客的内容包括: 一.垃圾收集器: 1,Serial 收集器: 2,ParNew 收集器: 3, Parallel Scavenge 收集器: 4,Serial Old收 ...

  6. 再谈GC2:Java垃圾收集器与GC日志分析实践

    4. GC 算法(实现篇) - GC参考手册 2017年02月05日 23:58:36 阅读数:6862 您应该已经阅读了前面的章节: 垃圾收集简介 - GC参考手册 Java中的垃圾收集 - GC参 ...

  7. 深入理解JVM - ZGC垃圾收集器

    如果下面的一些概念有些不清楚的可以先看深入理解JVM - 垃圾收集器和深入理解JVM - Shenandoah垃圾收集器. ZGC(Z Garbage Collector)是一款由Oracle公司研发 ...

  8. JVM面试重点总结(二)——垃圾收集器(GC)与内存分配策略

    1.如何判断对象已经死亡?可以作为"GC Root"对象的有哪些?怎样才能判断为真的死亡? 2.简述强.软.弱.虚引用? 3.垃圾收集算法分为哪几种?简述分代收集理论和分代收集的三 ...

  9. JVM学习----七种垃圾收集器(GC)

    文章目录 GC垃圾收集器 七种经典的垃圾回收器 查看默认垃圾收集器 新生代 Serial垃圾收集器(单线程. 复制算法) ParNew 垃圾收集器 (Serial的多线程版本. 复制算法) Paral ...

最新文章

  1. 【目标检测】yolo系列:从yolov1到yolov5之YOLOv1详解及复现
  2. 如何使用Leangoo看板统计中的任务分布?
  3. Vue build之后访问dist目录静态资源不加载问题解决
  4. boost::safe_numerics模块实现检测到的数据类型溢出的测试程序
  5. 类属性-类属性的定义及使用
  6. 工作总结7:自定义样式
  7. 【算法系列之十一】k个一组翻转链表
  8. angelababy机器人唱_被Angelababy、周震南等摸头杀?这个机器人为什么受宠
  9. sqltype java_【SQL参考】SQL数据类型与JAVA中type的对应
  10. 2017 ACM-ICPC乌鲁木齐网络赛 B. Out-out-control cars(几何)
  11. 在java EE版本eclipse下如何查看tomcat部署的位置 。版权声明:本文为博主原创文章,未经博主允许不得转载。...
  12. 创建、删除swap分区
  13. idm2021中文版序列号加速下载百度云网盘大文件教程
  14. JVM内存模型及CMS、G1和ZGC垃圾回收器详解
  15. kpi权重设置原则_绩效指标确定权重的原则
  16. Oracle错误处理机制
  17. 中国移动、联动、电信
  18. nats断链情况总结
  19. 高等数学--导数、偏导数、梯度简介
  20. 杂志风城市夜景PPT模板

热门文章

  1. 专题3:Django配置分布式路由
  2. 算法学习(2)----丢番图方程
  3. 清华镜像源地址(国内下载python包必备地址)
  4. 镜像站(整理各个镜像站资源)
  5. IC设计书籍信息收集
  6. 三菱FX5U连接MQTT
  7. 使用Arduino UNO以及好盈电调控制无刷电机
  8. 虚拟机复制镜像操作方法
  9. 电话号码被标记了怎么取消?标记取消最强攻略来了
  10. R语言七天入门教程二:认识变量与运算符