目录
- 一、内存分配原则
- 二、内存回收原则
-
- 1.GC类型
- 2.死亡对象判断方法
-
- [2.1 引用计数法](#2.1 引用计数法)
- [2.2 可达性分析算法](#2.2 可达性分析算法)
- 3.垃圾收集算法
-
- [3.1 标记-清除算法](#3.1 标记-清除算法)
- [3.2 标记-复制算法](#3.2 标记-复制算法)
- [3.3 标记-整理算法](#3.3 标记-整理算法)
- [3.4 分代收集算法](#3.4 分代收集算法)
一、内存分配原则

1.对象优先在Eden区分配
对象首先在Eden区分配内存空间。当Eden区没有足够空间进行分配时,JVM触发Minor GC:
- 将Eden区中所有存活对象的对象头部记录年龄的4bit位+1 ,并移动到
S1区。 - 将S0区的存活对象年龄+1,如果年龄达到
1111,将对象移动到Tenured区,否则移动到S1区。 - 如果S0区中相同年龄的所有对象大小的总和超过了S0空间的一半 ,那么年龄大于或等于该年龄的对象会移动到
Tenured区。 - 此时Eden和S0被清空,将S0和S1身份互换,下次GC移动到S0。
- 为新对象在
Eden区分配内存空间,初始化年龄为0000。 - 如果
S1区在Minor GC过程中空间不足,JVM会采用分配担保机制 ,将超出S1容量的对象直接移动到Tenured区 。如果Tenured区空间也不足,就会触发Full GC。
这里可能会问为什么要有Eden、S0、S1,直接用Eden不行吗?
首先这是一种垃圾回收算法,分区机制 是考虑到将存活的对象转移到新的连续内存空间中保证死亡对象回收后不会有内存碎片问题,导致空间不足。
其次这种转移方式是为了统一更新存活对象的年龄,在转移时更新避免遗漏。
2.需要大量连续内存空间的对象直接分配到Tenured区
大对象直接进入Tenured区是为了避免大对象在S0\S1区之间来回移动的成本 、避免占用太大的S1区内存 、大对象需要连续空间会导致**S1区内存碎片**,降低存储效率。
3.长期存活的对象转移到Tenured区
对象在S0\S1区每熬过一次Minor GC,对象的年龄就增加1岁,当它的年龄增加到一定程度(默认为 15 岁),就会被移动到Tenured区中。
二、内存回收原则
1.GC类型
Minor GC:Eden内存区 空间不足时触发,只清理Eden和S0、S1中的对象。Major GC:当Tenured内存区 空间不足时触发,只清理Tenured中的对象。Full GC:当方法区、Tenured内存区 空间不足时触发,清理整个堆空间(Eden、S0、S1、方法区Metaspace)
2.死亡对象判断方法
2.1 引用计数法
给对象中添加一个引用计数器,每当该对象被引用,计数器就加 1;当引用失效,计数器就减 1;计数器为 0 的对象默认为死亡。
这种方法的缺点是当多个对象相互引用,但是这些对象除了相互引用外没有被使用,此时GC就无法回收。
java
void test() {
Node a = new Node(); // 对象1
Node b = new Node(); // 对象2
a.next = b;
b.next = a;
// 方法结束,对象1和对象2就用不到了,但是由于他们相互引用,GC无法回收他们
}
2.2 可达性分析算法
将活跃栈帧、活跃线程对象 作为GC Roots,在JVM堆中(包括字符串常量池)只要是正在执行的方法、活跃线程中的强引用指向的对象 都不会被回收,没有与任何GC Roots关联的对象被定义为死亡,等待下次GC,死亡对象的内存空间就会被释放。
大白话就是只要方法中用到的堆对象就不会被回收(前提是强引用),方法执行完了实例化的对象就没用了,那就可以回收了。
java
void test() {
Node a = new Node(); // 对象1
Node b = new Node(); // 对象2
a.next = b;
b.next = a;
// 方法结束,栈帧被释放
// 虽然对象1和对象2互相引用,但没有GC Roots指向它们
// 结果:对象1和对象2都不可达,都会被回收
}
对于方法区中的类,判定为死亡需要满足三个条件:
- 该类所有在堆中的实例都已经被回收。
- 加载该类的ClassLoader已经被回收。
- 内存中没有使用反射访问该类。
3.垃圾收集算法
3.1 标记-清除算法
标记出存活的对象,标记完成后统一回收掉所有没有被标记的对象,缺点是内存碎片问题严重。

3.2 标记-复制算法
将内存分为大小相同的两块(Eden+P0、P1),只向Eden和P0中存数据,执行垃圾回收时将存活的对象全部转移到P1内存中,清除Eden+P0的,很好的解决了内存碎片问题。

3.3 标记-整理算法
用于Tenured区的内存回收,让所有存活的对象向一端移动(紧凑),然后清理掉端边界以外的内存。

3.4 分代收集算法
目前JVM使用的算法,就是对于Eden、P0、P1的内存空间使用标记-复制算法,对Tenured内存空间使用标记整理算法。