JVM虚拟机(三)垃圾回收简介、垃圾回收算法、分代回收、垃圾回收器种类、G1垃圾回收器
目录
- 一、什么是垃圾回收?
- 1.1 什么是垃圾回收?
- 1.2 什么对象能被垃圾回收?
- 1)引用计数法
- 2)可达性分析算法
- 二、JVM 垃圾回收算法
- 2.1 标记清除算法
- 2.2 标记整理算法(标记压缩算法)
- 2.3 复制算法
- 2.4 总结
- 三、JVM 的分代回收
- 3.1 堆中的区域划分
- 3.2 分代收集算法-工作机制
- 3.3 Minor GC、Mixed GC、Full GC 的区别是什么?
- 3.4 总结
- 四、JVM 有哪些垃圾回收器?
- 4.1 单线程垃圾收集器
- 4.2 多线程垃圾收集器
- 4.3 并发垃圾收集器
- 4.4 G1垃圾收集器
- 五、G1垃圾收集器
- 5.1 G1垃圾回收器的特点
- 5.2 垃圾回收的三个阶段
- 1)Young Collection(新生代垃圾回收)
- 2)Young Collection + Concurrent Mark(年轻代垃圾回收+并发标记)
- 3)Mixed Collection(混合垃圾回收)
- 5.2 Humongous区
- 六、总结

一、什么是垃圾回收?
1.1 什么是垃圾回收?
在介绍垃圾回收之前,我们需要先明确几个点:
- 为什么要进行垃圾回收呢?
- 回收哪里的垃圾呢?
是这样的,垃圾回收主要指的是回收堆中的对象,堆的位置如下图所示:

堆是一个共享区域,我们创建的对象和数组都存储在当前的堆里。但是我们也不能无限地去创建对象,也不是所有的对象都需要一直存在,如果说不进行垃圾回收的话,内存迟早都会被耗尽的,所以说及时的垃圾回收就显得非常有必要了。那什么对象才能被回收呢?
1.2 什么对象能被垃圾回收?
简单一句话就是:如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就可能会被垃圾回收器回收。
定位什么样的对象是垃圾有两种方式:第一个是引用计数法,第二个是可达性算法。
1)引用计数法
引用计数法:一个对象被引用了一次,在当前的对象头上递增一次引用次数,如果这个对象的引用次数为0,代表这个对象可回收。
比如下面这段代码:
String demo = new String("123");
因为我们在这里 new 了一个 String,所以会在堆中开辟一块空间去存储当前的对象:
这时,我们的引用计数法就会去增加一次引用的次数,ref=1。
假如目前的 demo 指向 null:
String demo = null;
这时,demo 就不会指向原来的那块内存了,当前的 ref 就会从 1 变成 0:
ref 变成 0 就表示当前的对象 new String(“123”) 是可以被垃圾回收的。
这个方法看起来非常简单,但是引用计数法也有一定的问题,我们来看下一个例子:
假如有下面这样一段代码:

我们先来分析一下这个代码:
- 左边代码中,Demo 类中有一个 Demo 的属性指向它自己,然后还有一个 String 的 name 和一个带参构造函数。
- 右边代码中,使用了 Demo 类创建了 a 和 b 两个实例,其中 a 的 instance 成员变量指向了 b,b 的 instance 成员变量指向了 a。
这段代码在内存中的表示方式如下:
首先,在栈中有一个变量 a 和一个变量 b,分别指向堆中的两块内存。由于除了 a 和 b 指向了这两块内存,它们之间还存在互相引用,只要有对象引用,引用次数就会加1,所以此时引用次数 ref=2。
下面我们继续执行代码:
代码中把 a 和 b 都指向 null,这时我们栈中的变量 a 和变量 b 就不再引用堆中那两块内存了,这样堆中两块内存的引用次数 ref 就会都变成 1 了,如下所示:
这时大家就会发现,目前堆中的这两个对象的引用次数都是1,但是目前这两个对象是没有人使用的,但是它们依然不会被回收。如果出现了这种情况,就是出现了 循环引用,就会引发内存泄漏。因为这两个对象一直不会被回收。
以上就是引用计数法,使用起来是非常简单,但是缺点就是容易导致内存泄漏。所以说一般不会采用这种方法去定位某个对象是否是垃圾。
下面我们看第二种定位垃圾的方式,可达性分析算法。
2)可达性分析算法
现在的虚拟机采用的都是通过可达性算法来确定哪些对象是垃圾。
首先,我们来看下面这张图:
最上面有一个 GC Roots,相当于是一个树根。从根中探索查看是否有关联的对象:
- 如果说能关联到,不管是直接关联(如A)还是间接关联(如B),找到的这些对象都是存活的对象,这些对象就不会被垃圾回收。
- 如果扫描堆的过程中,不能沿着 GC Roots 找到的对象,比如X、Y,它们目前没有与任何的 GC Root 进行关联,这两个就证明是可以被回收的对象。
对比我们刚才说的引用计数法,可达性分析算法能够更精确地定位哪些是可回收的垃圾。所以现在的虚拟机都是采用可达性分析算法来去确定哪些是垃圾。
其实这里还有一个问题,就是哪些对象可以作为 GC Root 呢?
一共有4种对象可以作为 GC Root:
-
虚拟机栈(栈帧中的本地变量表)中引用的对象: 比如下面这个示例中的 demo:
-
方法区中类静态属性引用的对象: 比如下面这个示例中,变量a 作为静态变量,它所引用的 new Demo() 也可以作为 GC Root。
-
方法区中常量引用的对象: 比如下面这个示例中,变量a 作为静态常量,这种情况下,变量a 所引用的 new Demo() 也是可以作为 GC Root 的。
-
本地方法栈中 JNI(即一般说的 Native 方法)引用的对象: 通过 JNI 接口由本地代码(如C/C++)持有的对象引用。
-
系统类加载器加载的类: 由系统类加载器加载的类,这些类本身以及它们通过静态字段持有的对象通常被认为是不可回收的。
在我们平时的开发过程中,用到前三种的对象类型会多一些。以上就是可达性分析算法。
二、JVM 垃圾回收算法
上面我们已经能够区分出内存中存活的对象和死亡的对象,GC接下来的任务就是去执行垃圾回收,释放掉那些无用对象所占用的内存空间,以便有足够的空间为新对象分配可用的内存。
目前 JVM 中就有三种比较常见的垃圾回收算法:
- 标记清除算法
- 复制算法
- 标记整理算法
这三种算法每一个都有各自的特点,下面一起了解一下。
2.1 标记清除算法
标记清除 是将垃圾回收分为 2 个阶段,分别是标记和清除:
- 对这些标记为可回收的内容进行 垃圾回收。
我们先来看下面这张图:
图中蓝色部分代表 “存活的对象”,灰色部分代表 “待回收的对象”,其他的都是 “空闲的空间”。
标记清除算法的第1步就是:根据可达性分析算法得出的垃圾进行 标记。
可达性分析算法就是使用 GC Root 去标记哪些是存活的对象,如下所示,可以看到蓝色部分都是被 GC Root 标记存活的对象:
标记清除算法的第2步就是:对这些标记为可回收的内容进行垃圾回收。
根据步骤,标记的对象都留下来,然后直接清除没有标记的内容就可以了。回收后的效果如下:
现在这个图中只留下了蓝色的部分,这些是做了标记,属于目标存活的对象,这个就是 标记清除算法。
- 优点: 标记和清除速度较快。
- 缺点: 内存碎片化较为严重,内存不连贯。
我们知道数组也是会存储到堆中,并且数组存储必须是一个连续的内存空间。如果我们使用了标记清除算法,由于内存不连续,有可能没有办法进行存储新的对象,也没有办法去存储一个比较大的数组。所以说这个算法用的相对比较少一些,用的比较多的主要是后面两种。
我们继续来看第二种:
2.2 标记整理算法(标记压缩算法)
“标记整理算法”,也称标记压缩算法,与我们刚才介绍的 “标记清除算法” 差不多,我们先看下面这张图:
首先,它也会通过 GC Root 去标记哪些对象是存活的对象,然后再去清除待回收的对象。但是它同时还多了一步,我们再来看下面这张图:
从图中可以看到,标记整理算法清除之后,它会把存活的对象进行整理,就是把所有的对象像一端移动。这样就避免了内存碎片化的问题了。但是由于标记整理算法多了一步,需要移动对象在内存中的位置,所以说它的性能也会收到一定的影响。
以上就是标记整理算法,很多老年代的垃圾回收器都是使用标记整理算法。
下面我们再来看最后一种垃圾回收算法:复制算法。
2.3 复制算法
复制算法 是将整个内存分成了大小相等的区域,标记阶段与我们前面的算法是类似的,也是通过 GC Root 进行标记哪些对象是存活的对象,然后将存活的对象进行复制,复制到另外一块内存区域。
当然,复制的过程中就自动完成了碎片的整理。如下图所示,就是回收之后的效果:
左边的 4 个对象被挪到了右边的区域,接下来把左边整个区域清空就可以了。
优点:
- 在垃圾对象多的情况下,效率较高;
- 清理后,内存无碎片。
缺点:
- 分配的 2 块内存空间,在同一个时刻,只能使用一半,内存使用率较低。
一般垃圾回收的时候,年轻代的垃圾回收器都会使用复制算法。
以上就是全部的三种垃圾回收算法了,下面我们进行一下总结。
2.4 总结
JVM 垃圾回收算法有哪些?
- 标记清除算法:垃圾回收分为 2 个阶段,分别是:标记、清除。效率高,有磁盘碎片,内存不连续。
- 标记整理算法:与标记清除算法一样,将存活的对象都向内存另一端 移动,然后清理边界以外的垃圾。无碎片,对象需要移动,效率低。
- 复制算法:将原有的内存空间一分为二,每次只用其中的一块。将正在使用的对象 复制 到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。无碎片,内存使用率低。
三、JVM 的分代回收
在之前介绍 JVM 组成的时候说话,在堆中分为新生代和老年代。下面我们就要详细说明一下,在垃圾回收的时候它们到底有什么作用。
3.1 堆中的区域划分
首先,我们还是要重新介绍一下堆中的组成:
- 在 Java8 中,堆被分成了两份:新生代和老年代(比例为1:2)
如下图所示:

左边的新生代占了 1/3,右边的老年占了 2/3。在新生代中又划分为了三份:
- 一个是伊甸园区Eden,新生的对象都分配到这里。
- 还有两个幸存者区Survivor,分为 from 和 to。
- Eden区、from区、to区比例为 8:1:1。
以上就是目前堆中的结构划分,下面我们来介绍一下垃圾产生之后它们是如何工作的。
3.2 分代收集算法-工作机制
- 首先,我们刚才介绍过,新生的对象都需要存储到 Eden 区,如下图所示:

- 当伊甸园区内存不足的时候(如上图所示已经占满了),这个时候 JVM 就会使用之前我们讲过的 “可达性分析算法” 来去标记 Eden 区和 from 区中存活的对象,当然目前 from 区中是没有对象的。
- 假如标记了 A 对象是存活的,接下来就会采用 “复制算法” 将 A 对象复制到 to 区中。复制完毕之后,Eden 区和 from 区都要清空掉。操作后如下所示:

- 假如经过一段时间后,Eden 区内存又出现不足,如下所示:

- 这时候依然会采用 “可达性分析算法” 去标记 Eden 区和 to 区中存活的对象,然后把这些对象复制到 from 区中。
- 假如现在 1 和 A 这两个对象依然存活,这时候就直接复制到了 from 区中,然后 Eden 区和 to 区都会清空掉。操作后如下所示:

- 又经过了一段时间,又有一些新的对象存到了 Eden 区中,如下所示:

- 假如这时候 A 对象被挪动的次数太多,比如超过了 15 次,那么这时候 A 对象就不会再在 from 区和 to 区之间挪过来挪过去了。它会直接把 A 对象存储到 老年代 中。因为这种情况下,我们一般认为 A 对象会被一直引用着,它的存活时间会更长一些。
- 当然还有一种情况,假如我们的幸存者区已经内存不足了,或者说当前某个对象太大了,它也会提前晋升到老年代。例如下图中,A 对象代表挪动次数超过 15 次之后进入了老年代;而 w 对象是一个新生的对象,它并没有到达一定的挪动次数,所以 w 对象是正常地在新生代中进行复制挪动,等 w 对象挪动到一定次数也会进入到老年代中。

以上就是新生代和老年代工作的配合方式,但是在实际面试过程中还会问一些名词,比如:MinorGC、Mixed GC、FullGC。
3.3 Minor GC、Mixed GC、Full GC 的区别是什么?
我们前面了解到新生代和老年代的比例如下所示:

Minor GC,也称 Young GC,发生在新生代的垃圾回收,暂停时间短(STW)。Mixed GC,新生代 + 老年代两块 部分 区域的垃圾回收,G1 收集器特有。Full GC,新生代+老年代 完整 垃圾回收,暂停时间长(STW),应尽量避免。
名词解释:
STW(Stop-The-World):,暂停所有应用程序线程,等待垃圾回收的完成。Minor GC 暂停时间短就说明效率比较高,如果暂停时间比较长就说明效率降低。
3.4 总结
1)堆的区域划分
- 堆被分为了两份:新生代和老年代【1:2】。
- 对于新生代,内部又被分为了三个区域:Eden区,幸存者区survivor(分成 from 和 to)【8:1:1】。
2)对象分代回收策略
- 新创建的对象,都会先分配到 Eden 区。
- 当 Eden 区内存不足,标记 Eden 区与 from 区(现阶段没有)的存活对象。
- 将存活对象采用复制算法复制到 to 区中,复制完毕后,Eden 区和 from 内存都得到释放。
- 经过一段时间后,Eden 区的内存又出现不足,标记 Eden 区和 to 区存活的对象,将其复制到 from 区。
- 当幸存者区对象熬过几次回收(最多15次),晋升到老年代(幸存者区内存不足或大对象会提前晋升)。
四、JVM 有哪些垃圾回收器?
在 jvm 中,实现了多种垃圾收集器,包括:
- 单线程垃圾收集器(Serial 和 Serial Old)。
- 多线程垃圾收集器(Parallel Scavenge 和 Parallel Old),JDK8默认。
- 并发垃圾收集器(ParNew 和 CMS)。
- 智能并发垃圾收集器(G1),JDK9默认。
我们根据新生代和老年代可以进行如下分类:
- 新生代收集器: Serial、ParNew、Parallel Scavenge。
- 老年代收集器: Serial Old、CMS、Parallel Old。
- 通用收集器: G1。
常用组合:Serial + Serial Old,Parallel Scavenge + Parallel Old,ParNew + CMS,G1(不需要组合)
4.1 单线程垃圾收集器
单线程垃圾收集器,也称串行垃圾收集器,包含了两个收集器:Serial 和 Serial Old,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑。
- Serial: 作用于新生代,采用复制算法。
- Serial Old: 作用于老年代,采用标记-整理算法。
垃圾回收时,只有一个线程在工作,并且 Java 应用中的所有线程都要暂停(STW),等待垃圾回收的完成。
举个例子,如下图所示:

当前一个电脑里面有4核 CPU,绿色的线表示我们的程序在正常的运行,当到达安全点后就会产生一个垃圾回收。这时候Serial 垃圾回收器会暂停其它线程,保留一个专门的线程来进行垃圾回收。等垃圾回收线程执行完了垃圾回收之后,才能恢复运行程序的线程继续运行。
4.2 多线程垃圾收集器
多线程垃圾收集器,也称并行垃圾收集器,也包含了两个收集器:Parallel New 和 Parallel Old。JDK8默认使用此垃圾回收器。
- Parallel New: 作用于新生代,采用复制算法。
- Parallel Old: 作用于老年代,采用标记-整理算法。
垃圾回收时,多个线程在工作,并且 Java 应用中的所有线程都要暂停(STW),等待垃圾回收的完成。
举个例子,如下图所示:

当前一个电脑里面有4核 CPU,对应的多个线程都在同时进行垃圾回收,对比我们刚才说的串行垃圾回收器,有更多的线程参与了垃圾回收。这个性能的确会更高一些,但是在进行垃圾回收的时候,依然也会进行线程的中断(STW)。JDK8 默认使用这种垃圾回收器
4.3 并发垃圾收集器
并发垃圾收集器,也包含了两个垃圾收集器:ParNew 和 CMS。CMS 全程 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器。该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行。
举个例子,如下图所示:

当前一个电脑里面有4核 CPU:
-
在进行垃圾回收的时候,首先它会有一个初始标记,这个标记就是使用我们之前讲过的 “可达性分析算法” 来标记存活的对象的,这时候会进行 STW,即标记的时候其他线程会进行阻塞状态。
-
然后再进行并发标记,重新标记。为了方便理解,我们看下面这张图:
-
情况一:当我们的代码在执行过程中,有可能在并发标记完成之后,本来 X 被认为是一个垃圾对象,但是在并发标记阶段的同时,我们的代码是可以正常运行的,也有可能会出现新的引用,比如:A对象又去引用了 X对象。那这个时候 X对象就不能被回收了,所以说我们在这里有一个重新标记的阶段。
-
情况二:比如说,在一开始标记的时候,当前的 D对象的确是一个存活的对象,JVM 在重新标记的过程中,B对象取消了对 D对象的引用,那么此时的 D对象就要把它作为一个垃圾对象进行回收。
以上两种情况就体现了重新标记的重要作用,我们再回到并发垃圾收集器的回收过程:
- 重新标记完成之后,才真正进入到并发清理的阶段。从图中可以看到,并发清理的过程中,其他线程还是可以正常运行的。
关于并发垃圾收集器,主要关注点在于停顿时间短,记住这点即可。
4.4 G1垃圾收集器
由于 G1 垃圾收集器比较重要,也是面试官常问的内容,我们单独作为一个章节进行讲解。
五、G1垃圾收集器
G1垃圾收集器 是一种智能并发垃圾收集,应用于新生代和老年代,在 JDK9 之后默认使用 G1。
5.1 G1垃圾回收器的特点
- 在 G1 垃圾收集器中划分了多个区域,每个区域都可以充当 Eden区、Survivor区、Old(老年代)、Humongous,其中 humongous 专为大对象准备。这里面并没有之前的老年代和新生代的比例划分,也没有新生代中的比例划分,每个区域都一样,并且每个区域可以存储各种对象,还专门增加了一个 humongous 区用来存储大对象。
- 采用复制算法,没有内存碎片。
- 响应时间与吞吐量兼顾,即效率高的同时处理任务比较多。
- G1垃圾收集器在垃圾回收的过程中主要分为三个阶段:新生代回收、并发标记、混合收集。如下图所示:
- 如果并发失败(或者说垃圾回收失败,即回收速度赶不上创建新对象速度),会触发 FullGC。
以上就是 G1 垃圾回收器的特点了,下面我们就从 G1垃圾回收器的工作流程入手,进行了解。
5.2 垃圾回收的三个阶段
1)Young Collection(新生代垃圾回收)
首先,我们来看一张图:
这张图表示了整个堆空间,我们把整个堆划分成了大小相等的区域。其中的每个区域都可以作为我们刚才提到的 Eden区、Survivor区、Old(老年代)、Humongous。
- 初始时,所有区域都处于空闲状态。
- 当创建对象时,就会挑出一些空闲区域作为 Eden区来存储这些对象。比如下图中就表明了几个 E,这些就是我们的 Eden区:
- 随着对象越来越多,堆中的 Eden区可能快要放满了,这时就会触发一个新生代的垃圾回收。
大家可能有疑问:还有这么多的空间不去存储就开始垃圾回收了嘛?是这样的,G1垃圾收集器中新生代的占比不是固定的,它是在 5% ~ 6% 之间波动,G1会自动进行调整。但是大家可以思考一下,既然新生代的大小是被限制的,不管怎么波动都会有限制,都会限制当前的 Eden 区的大小,我们就不能随便地去创建新对象。所以说当我们的 Eden 区的数量达到一定程度后,它就会触发一次 Eden 区的垃圾回收。它也是采用复制算法,即会使用 “可达性分析算法” 去标明哪些对象是存活下来的。把所有存活的对象进行标记之后,没有标记的对象就成为垃圾了。G1就会将这些存活的对象用复制算法复制到 Survivor区中。
如下图所示:
- 图中的 s 其实就是 Survivor 区,经过标记之后,将剩下的存活对象都复制到幸存者区,然后之前的 Eden 区就可以释放掉了,如下图所示:
大家注意,在标记的过程当中或者是刚刚复制的过程中,都需要暂停,即触发一个 STW。不过因为我们幸存者的对象相对来说是比较少的,所以暂停时间也不会太长。
以上就是新生代的第一次垃圾回收。随着时间的流逝,还会产生很多的对象,如下图所示:
后续产生的对象都会分配到 Eden区,所以 Eden区的内存就逐渐耗尽了。这时垃圾回收器就会把 Eden区中的幸存者对象和 Survivor区中的幸存者对象合并到一起,复制到一个新的幸存者区中。如下图所示:
图中出现了一个新的幸存者区,然后新的幸存者区和之前的幸存者区就会进行一次复制。
当然,这里还有一种情况,如果当前的幸存者区中有一些对象已经超过了阈值,这些比较老的对象就会晋升到老年代中。也就是说,如果一个对象经过了很多次的垃圾回收,它就会晋升到老年代,另外一部分幸存者区和伊甸园区中的幸存者对象就会复制到一个新的幸存者区中。这样一来,伊甸园区和上一次的幸存者区就可以释放了。
以上就是垃圾回收的第一个阶段:年轻代的垃圾回收。下面我们进入第二个阶段:并发的标记阶段。
2)Young Collection + Concurrent Mark(年轻代垃圾回收+并发标记)
在当前的阶段中有一个触发条件:
- 当老年代占用内存超过阈值(默认是45%)后,触发并发标记,这时无需暂停用户线程。
其实,在老年代中找到那些存活的对象,并且给它们加上标记,这个过程是并发执行的,在此期间不会暂停用户的线程。当然这个阶段也有一次重新标记,需要处理那些漏标的对象,所以说还是需要一次 STW 的,即需要暂停用户线程。这个就是并发标记的阶段了。
并发标记完成以后,就可以进入到第三个阶段了,也就是混合收集阶段。
3)Mixed Collection(混合垃圾回收)
在混合收集阶段,它并不是一次性收集了所有的老年代的内存,那样暂停的时间会比较长,达不到预期的暂停时间。在 JVM 里面设置了一个预期的暂停时间,比如暂停时间不能超过XXms,这个是可以设置的。为了让老年代的垃圾回收不超过预期的时间,它并不是一次性把所有的老年代都进行回收,而是先挑选出回收价值比较高的对象。比如下图中标红的几个老年代:
它们里面存活的对象数量比较少,所以回收的价值比较高,即进行少量的回收就可以获得同样的内存空间。相反,其它的一些老年代对象因为存活对象比较多,回收的价值就比较低了。因此它会挑选回收价值比较高的老年代,连同我们的 Eden区和 Survivor区一起来做一次垃圾回收。这个就是混合收集(既包含了新生代的垃圾回收,也包含了老年代的垃圾回收)。
如下图所示:
在混合收集阶段中,Eden区和原有 Survivor区中存活的对象会复制到一个新的 Survivor区,而 Survivor区中到达一定挪动次数的存活对象和原有老年代中的存活对象会复制到一个新的老年代中。等混合收集完成之后,内存就得到释放了,如下图所示:
复制完成,内存得到释放,这样就完成了一次混合收集。
当然,混合收集可能要重复执行多次。因为第一次我们在预期的时间内可能会收到了刚才的三个老年代区,后续有可能再多执行几次混合收集,把剩下的老年代再重新标记,再将标记的内存逐渐地释放出来。这个就是我们第三阶段的混合收集了。
当进行了多次混合收集之后,又会进入下一轮的新生代回收、并发标记、混合收集。
以上就是 G1 垃圾回收器的三个阶段。JVM 里面还有可能出现并发失败的情况,也就是我们垃圾回收的速度小于我们创建新对象的速度,此时就会触发一次 FullGC,它的暂停时间就非常久了。但是经历了多次的 G1 垃圾回收器的三个阶段回收,它们都是并发执行的,用户的暂停时间是比较短的。一般是不会触发 FullGC 的。
5.2 Humongous区
其实还有这里一个问题:如果说一个对象太大了,一个区域放不下的话,会存入到一个 Humongous区中。如果说一块区域不够的话,会分配一块连续的区域进行存储大对象,如下图所示:
讲到这里,G1 垃圾回收器就讲完了,下面我们进行下总结。
六、总结
详细聊一下 G1 垃圾回收器:
- 应用于新生代和老年代,在 JDK9 之后默认使用 G1。
- 划分为多个区域,每个区域都可以充当 Eden、Survivor、Old、Humongous 区,其中 Humongous 区专为大对象准备。
- 采用复制算法。
- 响应时间与吞吐量兼顾。
- 分成三个阶段:新生代回收(STW)、并发标记(重新标记 STW)、混合收集。
- 如果并发失败(即回收速度赶不上创建新对象速度),会触发 FullGC。
整理完毕,完结撒花~🌻
参考地址:
1.新版Java面试专题视频教程,java八股文面试全套真题+深度详解(含大厂高频面试真题),https://www.bilibili.com/video/BV1yT411H7YK
相关文章:
JVM虚拟机(三)垃圾回收简介、垃圾回收算法、分代回收、垃圾回收器种类、G1垃圾回收器
目录 一、什么是垃圾回收?1.1 什么是垃圾回收?1.2 什么对象能被垃圾回收?1)引用计数法2)可达性分析算法 二、JVM 垃圾回收算法2.1 标记清除算法2.2 标记整理算法(标记压缩算法)2.3 复制算法2.4 …...
JavaScript基础:js介绍、变量、数据类型以及类型转换
目录 介绍 引入方式 内部方式 外部形式 注释和结束符 单行注释 多行注释 结束符 输入和输出 输出 输入 变量 声明 赋值 关键字 变量名命名规则 常量 数据类型 数值类型 字符串类型 布尔类型 undefined 类型转换 隐式转换 显式转换 Number ✨介绍 &a…...
【牛客SQL快速入门】SQL基础(三)
一、条件函数 IF 条件函数 IF函数是最常用到的条件函数,写法为 if(xn,a,b),xn代表判断条件,如果xn时,那么结果返回a,否则返回b。 -- 把非北京大学的用户统一归为其他大学 Select device_id,if(university ‘北京大…...
Pytorch手撸Attention
Pytorch手撸Attention 注释写的很详细了,对照着公式比较下更好理解,可以参考一下知乎的文章 注意力机制 import torch import torch.nn as nn import torch.nn.functional as Fclass SelfAttention(nn.Module):def __init__(self, embed_size):super(S…...
PyCharm 2024.1 发布:全面升级,助力高效编程!
PyCharm 2024.1 发布:全面升级,助力高效编程! 文章目录 PyCharm 2024.1 发布:全面升级,助力高效编程!摘要引言 Hugging Face:模型和数据集的快速文档预览针对 JavaScript 和 TypeScript 的全行代…...
Nginx基础(06)
Nginx基础(05) uWSGI 介绍 uWSGI 是一个 Web服务器 主要用途是将Web应用程序部署到生产环境中 可以用来连接Nginx服务与Python动态网站 1. 用 uWSGI 部署 Python 网站项目 配置 Nginx 使其可以将动态访问转交给 uWSGI 安装 python 工具及依赖 安…...
【Qt 学习笔记】QWidget的windowOpacity属性 | cursor属性 | font属性
博客主页:Duck Bro 博客主页系列专栏:Qt 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ QWidget的windowOpacity属性 | cursor属性 | font属性 文章编号&#…...
Python爬虫:requests模块的基本使用
学习目标: 了解 requests模块的介绍掌握 requests的基本使用掌握 response常见的属性掌握 requests.text和content的区别掌握 解决网页的解码问题掌握 requests模块发送带headers的请求掌握 requests模块发送带参数的get请求 1 为什么要重点学习requests模块&…...
C++traits
traits C的标准库提供了<type_traits>,它定义了一些编译时基于模板类的接口用于查询、修改类型的特征:输入的时类型,输出与该类型相关的属性 通过type_traits技术编译器可以回答一系列问题:它是否为数值类型?是否为函数对象…...
gitee和idea集成
1 集成插件 2 配置账号密码 3 直接将项目传到仓库 4直接从gitee下载项目...
阿维·威格德森(Avi Wigderson)研究成果对人工智能领域的应用有哪些影响
AI人工智能的影响 威格德森(Avi Wigderson)的研究成果对人工智能领域的应用产生了深远的影响。 首先,威格德森在计算复杂性理论、算法和优化方面的贡献为人工智能领域提供了高效、准确的计算模型和算法。他的研究帮助我们更好地理解计算问题…...
【免费领取源码】可直接复用的医院管理系统!
今天给大家分享一套基于SpringbootVue的医院管理系统源码,在实际项目中可以直接复用。(免费提供,文中自取) 系统运行图(设计报告和接口文档) 1、后台管理页面 2、排班管理页面 3、设计报告包含接口文档 源码免费领取方式 后台私信…...
leetcode代码记录(全排列 II
目录 1. 题目:2. 我的代码:小结: 1. 题目: 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 示例 1: 输入:nums [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1…...
【数据结构与算法】之双向链表及其实现!
个人主页:秋风起,再归来~ 数据结构与算法 个人格言:悟已往之不谏,知来者犹可追 克心守己,律己则安! 目录 1、双向链表的结构及概念 2、双向链表的实现 2.1 要实现的接口…...
记一次奇妙的某个edu渗透测试
前话: 对登录方法的轻视造成一系列的漏洞出现,对接口确实鉴权造成大量的信息泄露。从小程序到web端网址的奇妙的测试就此开始。(文章厚码,请见谅) 1. 寻找到目标站点的小程序 进入登录发现只需要姓名加学工号就能成功…...
设计模式学习笔记 - 设计模式与范式 -总结:1.回顾23中设计模式的原理、背后的思想、应用场景等
1.创建型设计模式 创建型设计模式包括:单例模式、工厂模式、建造者模式、原型模式。它主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。 1.单例模式 单例模式用来创建全局唯一的对象。一个类只允许创建一个对象…...
22 文件系统
了解了被打开的文件,肯定还有没被打开的文件,就是磁盘上的文件。先从磁盘开始认识 磁盘 概念 内存是掉电易失存储介质,磁盘是永久性存储介质 磁盘的种类有SSD,U盘,flash卡,光盘,磁带。磁盘是…...
OVITO-2.9版本
关注 M r . m a t e r i a l , \color{Violet} \rm Mr.material\ , Mr.material , 更 \color{red}{更} 更 多 \color{blue}{多} 多 精 \color{orange}{精} 精 彩 \color{green}{彩} 彩! 主要专栏内容包括: †《LAMMPS小技巧》: ‾ \textbf…...
【Java开发指南 | 第一篇】类、对象基础概念及Java特征
读者可订阅专栏:Java开发指南 |【CSDN秋说】 文章目录 类、对象基础概念Java特征 Java 是一种面向对象的编程语言,它主要通过类和对象来组织和管理代码。 类、对象基础概念 类:类是一个模板,它描述一类对象的行为和状态。例如水…...
Neo4j 图形数据库中有哪些构建块?
Neo4j 图形数据库具有以下构建块 - 节点属性关系标签数据浏览器 节点 节点是 Graph 的基本单位。 它包含具有键值对的属性,如下图所示。 NEmployee 节点 在这里,节点 Name "Employee" ,它包含一组属性作为键值对。 属性 属性是…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
Linux安全加固:从攻防视角构建系统免疫
Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...
