垃圾回收
在垃圾回收中,我们需要判断三个条件,什么时候回收?在哪里回收?以及如何回收
1:hotspot的GC分类
- partial GC,并不收集整个GC堆的模式
- young GC,只收集年轻代的GC
- old GC,只收集老年代的GC,只有CMS中的concurrent collection是这个模式
- mixed GC,收集整个年轻代以及部分老年代的GC,只有G1是这个模式
- full GC,收集整个堆
- 包括young,old,永久代(如果存在的话)
- major GC,通常和full GC 是等价的,收集整个GC堆,有人说 major GC的时候,一定要问一下是上面的ull GC,还是 old GC
2:判断对象死亡
- 引用计数法
- 给一个对象加上一个引用计数器,每次引用数字加一,引用失效数字减一
- 计数器为0 的时候,对象不可能被使用
- 循环依赖的问题
- 引用的类型
- 强引用
- 生活中的必备品
- 只有强引用是包内可见的,其他的都是public
- 普通创建的对象默认都是强引用的,在JVM中,即使抛出OOM,强引用也不会被回收
- 弱引用
- 可有可无的商品
- 内存敏感的高速缓存
- 在内存不够的时候,他会为了避免抛出OOM,而回收掉弱引用
- 软引用
- 在任何时间都可能会被清除掉
- 只要一旦被 GC看到,就会被清除
- 虚引用
- 形同虚设
- 跟踪对象被垃圾回收的活动
- 强引用
- 可达性质分析
- 从GCroots开始,从该点开始向下搜索,走过的称为引用链。如果一个对象没有在引用恋上,说明是不可达的
- 虚拟机栈中的引用的对象
- 本地方法栈(native)中引用的对象
- 本地区中类静态属性引用的对象
- 本地区中常量引用的对象
- 所有被同步锁持有的对象
- 不可达的并非非死不可
- 不可达的对象,可以看作是死缓
- 此对象是否有必要执行inalize方法,
- 当对象没有覆盖finalize的时候
- finalize 已经被虚拟机调用的时候
- finalize方法
- finalize 是object的方法,当垃圾回收回收掉对象之前,就会调用这个的方法,处理最后的后事
- 如果它覆盖了finalize方法,并且在调用的时候,引用到了GC roots链上,此时就活了
- finalize 只能调用一次,
- finalize 是object的方法,当垃圾回收回收掉对象之前,就会调用这个的方法,处理最后的后事
- 从GCroots开始,从该点开始向下搜索,走过的称为引用链。如果一个对象没有在引用恋上,说明是不可达的
3:常量是废弃常量?
- 运行时常量池主要存放的是常量,那么,我们如何判断一个常量是不是废弃常量呢?
- JDK 1.7之前,常量池放在方法区,因此对方法区的实现是永久代
- JDK1.7,字符串常量池被拿到了堆中,而其他的常量池还在方法区,也就是永久代
- 1.8,移除了永久代用元空间来取而代之,运行时的字符串常量池还在堆中,运行时常量池还在方法区,只不过方法区由永久代变成了元空间
- 假设字符串常量池中有“”abc“,如果当前没有任何的string对象引用该对象的话,那么说明就是废弃对象。
4:类是一个无用的类?
- 该类的所有实例已经回收,即在堆中不存在任何的类对象
- 加载该类的classLoader已经被回收
- 该类的java.lang.Class没有在任何对象引用过,无法在任何对方通过反射访问该类的方法
- 在满足上面三个条件之后,JVM可以对该类进行回收,但是也并不绝对。
5:垃圾回收算法
- 标记清除
- 标记出所有不需要回收的对象
- 清除掉所有没有标记的对象
- 简单
- 存在内碎片
- 标记整理
- 标记出所有不需要回收的算法
- 让存活者的进行移动,使得不存在内存碎片
- 清除掉剩余的空间即可
- 复制
- 内存分为两块
- 每次使用其中的一块,当这一块使用完之后,就将活着的对象复制到另外一边去
- 再把这边的对象整体清除掉
- 效率高
- 浪费空间
- 分代回收
- 根据对象的年龄,来综合的使用上述各种垃圾回收方法
- 在新生代,死亡的概率是很高的,选择复制算法,每次复制少量的对象,并且效率高
- 在老年代,成活的效率比较高,使用标记清除,或者是标记整理的算法进行收集
- 过程
- 新生代和老年代,大概约为1:2
- 新生代中又分为伊甸区,from survivor,to survivor,默认是8:1:1
- 新生的对象都是在伊甸区,每次清除的时候会将伊甸区和 from survivor 中存活的对象移动到 to survivor中
- 清除 伊甸区和 from survivor,再把 to survivor中的对象移动到from survivor
- 老生代当达到某个数值的时候就会出发全局的垃圾回收
- 年龄
- 每次from survivor 和 to survivor之间的互相移动,年龄加一
- 当年龄达到15的时候,就移动到老年区,因为在mark word 中年龄是 4位
- 大对象直接在老年区
6:垃圾收集器
-
serial(串行) 收集器
- 使用一条线程进行垃圾收集工作
- 进行垃圾回收的时候会暂停其他的线程(stop the world)
- 优点
- 简单,高效
- 由于没有线程的交互开销,可以获得很高的单线程收集效率
- 运行在client 模式下的虚拟机是一个很不错的选择
- 优点
-
parnew 收集器
- 就是串行收集器的 多线程版本,和serial一模一样
- 新生代采用标记——复制算法,而老年代采用标记,清除算法
- 运行在server端的首要选择
- 只有serial,和parnew配合工作
-
parallel scavenge 收集器
- 关注的是吞吐量,高效率的使用CPU
- 新生代采用标记复制算法,老年代采用标记整理算法
-
CMS 收集器
- 获取最短回收停顿时间为目标的收集器,符合在用户体验的使用
- 第一款的并发收集器,实现了垃圾回收和用户线程同时工作
- 初始标记:暂停所有的线程,记录下和GC roots 相连的对象
- 并发标记:开启GC 和用户线程,用一个闭包的结构去记录可达对象,
- 重新标记:修正在并发标记阶段因为用户程序运行导致的变动标记记录
- 并发清除:开启用户线程,同时GC线程开始对未标记的区域做清扫
- 优点
- 并发标记 停顿小
- 缺点
- 对CPU 资源敏感
- 无法处理浮动的额垃圾
- 标记清除,导致有大量的内存碎片
-
G1 收集器
- 面向服务器的垃圾回收器,针对多核处理器和大内存的机器
- 并发和并行:G1能够充分的利用CPU,多核,大内存的硬件优势
- 分代收集:也采用了分代回收的机制
- 空间整合:G1从整体上来看是使用标记整理,从局部上是标记复制
- 可以预测的停顿:能偶建立可预测的停顿时间模型,
- 过程
- 初始标记
- 并发标记
- 最终标记
- 筛选回归
- 维护了一个优先队列,每次根据收集时间,以此选择价值最大的垃圾
- garage First,保证了在有限的时间内能够获得最大价值的垃圾回收
- 过程
-
ZGC 收集器
- stop the world 的情况会更少
2021.3.12.12:09