文章目录
垃圾回收
内存管理
堆中的对象的管理,对象空间的分配和释放
对象分配:
new
对象释放:
null,垃圾回收器负责回收所有不可达对象的内存空间
垃圾回收过程
-
发现无用对象(没有任何变量引用的对象)
-
回收无用对象占用的内存空间
如何判断是垃圾
1. 引用计数法
算法简单,循环引用的无用对象无法识别
public class Student {
String name;
Student friend;
public static void main(String[ ] args) {
Students1 = new Student0;
Student s2 = new Student0;
s1.friend = s2;
s2.friend = s1;
s1 = null;
s2 = null;
}
}
2. 可达性分析算法
通过对象的引用链是否可达来决定对象是否要被回收
从一系列的称为GC ROOT的对象为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Root没有任何引用链相连的话,则证明此对象不可达,即认为他是不可用的。
- 什么对象可以作为GC ROOT
- 虚拟机栈中的引用对象
- 方法区中的常量引用对象
- 方法区中的类静态属性引用对象
- 本地方法栈中的引用对象
- 活跃线程中的引用对象
垃圾回收算法
1. 标记-清除算法
标记:从根集合进行扫描,对存活的对象进行标记。
清除:对堆内存进行遍历,回收不可达对象内存。
缺点:清除后会产生大量不连续的内存碎片,可能导致后续在创建较大对象是无法找到足够的连续内存而触发再一次的垃圾回收
2. 复制算法
将可用内存分为对象面和空闲面,在对象面.上创建对象,当对象面没有空间的时候,将还存活的对象复制到空闲面,将对象面所有对象清除。
解决了碎片化问题,顺序分配内存,简单高效,适用于对象存活率较低的场景,因为复制的内容少,所以效率高。
-
标记-整理算法
标记:从根集合进行扫描,对存活的对象进行标记。
清除:移动所有存活的对象,按内存地址依次排列,然后将末端地址以后的内存全部回收。在标记-清除的基础上完成了移动,解决了内存碎片的问题,但是成本更高,适用于对象存活率较高的场景
-
分代搜集
主流回收算法,,组合回收机制
将不同生命周期的对象分配到内存堆中的不同区域,采用不同的垃圾回收算法,提高JVM效率
年轻代
Minor GC进行回收,采用复制算法,分为Eden区和Survivor区
Eden区:对象刚被创建的时候,存放在Eden区,如果Eden区放不下,则放在Survivor区,甚至老年代中
Survivor区:Minor回收时使用,将Eden中存活的对象存入Survior From中,再一次Minor时,将Survior From中的对象存入Survior To中,清除Survivor From,下一次Minor时重复此步骤,Survior From变成Survior To,Survior To变成Survior From,每次Minor,对象的年龄都+1,到达一定年龄,移动到老年代中。
(每次将Survior From的移动到Survior To区,移动完成之后清除Survior From区,然后两个区域变换名称)
老年代
存放生命周期较长的对象,使用标记-清理算法或者标记-整理算法进行回收
垃圾收集器
年轻代垃圾收集器
-
Serial收集器(复制算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程
-
ParNew收集器(复制算法):多线程收集,垃圾收集和工作线程可以同时进行
-
Parallel Scavenge收集器(复制算法):多线程收集,更关注系统的吞吐量
Serial收集器和ParNew收集器更关注用户线程停顿时间,停顿越短,响应越快,用户体验越好,适用于直接与用户交互的程序
Parallel Scavenge收集器更关注吞吐量,可提升CPU效率,尽快完成运算任务,适合在后台运行,不需要太多交互的程序
老年代垃圾收集器
- Serial Old收集器(标记-整理算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程
- ParNew Old收集器(标记-整理算法):多线程收集,垃圾收集和工作线程可以同时进行,吞吐量优先
- CMS收集器(标记-清除算法):垃圾回收线程和工作线程几乎可以同时工作
- Garbage First收集器(复制+标记-整理算法):并发和并行,使用多个CPU来缩短Stop-the-World(除了GC线程外,所有线程都处于等待状态,直到GC完成)的停顿时间,与用户线程并发执行,且采用不同方式去处理新对象,同时有利于空间整合,基于标记-整理算法,可以解决内存碎片问题