前言
本章回顾的是:GC垃圾回收机制原理。
想回顾JVM的可以看这篇文章:[Java]一篇文章浅入深出了解「JVM原理」以及常见问题
常更新,欢迎收藏~
GC
通常我们判断一个对象判定为垃圾的标准,是有没有被其他对象引用。
判定对象是否为垃圾的算法
-
引用计数算法
- 但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。看一段代码。
public class ReferenceCountingGC { private Object instance = null; private static final int _1MB = 1024 * 1024; /** 这个成员属性唯一的作用就是占用一点内存 */ private byte[] bigSize = new byte[2 * _1MB]; public static void main(String[] args) { ReferenceCountingGC objectA = new ReferenceCountingGC(); ReferenceCountingGC objectB = new ReferenceCountingGC(); objectA.instance = objectB; objectB.instance = objectA; objectA = null; objectB = null; System.gc(); } }
-
可达性分析算法
这个算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。
那么问题又来了,如何选取GCRoots对象呢?在Java语言中,可以作为GCRoots的对象包括下面几种:
-
虚拟机栈(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。
-
方法区中的类静态属性引用的对象。
-
方法区中常量引用的对象。
-
本地方法栈中JNI(Native方法)引用的对象。
-
方法区的垃圾回收
方法区的垃圾回收主要回收两部分内容:1. 废弃常量。2. 无用的类。
如何判断无用的类呢?需要满足以下三个条件
-
该类的所有实例都已经被回收,即Java堆中不存在该类的任何实例。
-
加载该类的ClassLoader已经被回收。
-
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
谈谈你了解的垃圾回收算法
-
标记-清除算法(Mark and Sweep)
- 分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。
-
复制算法(Copying)
- 它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次性清理掉。
-
标记-整理算法(Compacting)
- 让所有存活对象都向一端移动,然后直接清理掉边界以外的内存
-
分代收集算法(Generational Collector)
- **新生代的内存被划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。**每次回收时,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden区和Survivor区的比例为8:1,意思是每次新生代中可用内存空间为整个新生代容量的90%。当然,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时,需要依赖老年代进行分配担保(Handle Promotion)。
- 大批对象死去、少量对象存活的(新生代),使用复制算法,复制成本低;对象存活率高、没有额外空间进行分配担保的(老年代),采用标记-清理算法或者标记-整理算法。
- GC(Generational Collector)分类
-
Minor GC:新生代GC,每次 Minor GC 会清理年轻代的内存。触发机制:
- 当年轻代满时就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。
-
Full GC:老年代 GC, 是清理整个堆空间—包括年轻代和永久代。触发机制:
- 调用System.gc()时,系统建议执行Full GC,但是不必然执行
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
- 由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
-
Major GC:是清理老年代。
-
- 常用的调优参数
- -XX:SurvivoRatio:Eden和Survivor的比值,默认8:1
- -XX:NewRatio:老年代和年轻代内存大小的比例
- -XX:MaxTenuringThreshold:对象从年轻代晋升老生代经过GC次数的最大阙值
- 参考
垃圾收集器
垃圾收集器就是上面讲的理论知识的具体实现了。不同虚拟机所提供的垃圾收集器可能会有很大差别,我们使用的是HotSpot,HotSpot这个虚拟机所包含的所有收集器如图:
- 新生代
- Serial收集器
- ParNew收集器
- Parallel Scavenge收集器
- 老生代
- Serial Old收集器
- Parallel Old收集器
- CMS收集器
- G1收集器
Object的finalize()方法的作用是否与C++的析构函数作用相同
-
与C++的析构函数不同,析构函数调用确定,而它不确定
-
将未被引用对象放置于F-Queue队列
-
方法执行随时可能会被终止
-
给予对象最后一次重生机会
-
通过代码理解
package com.dai; /** * author daioo * create 2019-03-07 22:45 */ public class Finalization { public static Finalization finalization; @Override protected void finalize() { System.out.println("Finalized"); finalization = this; } public static void main(String[] args) { Finalization f = new Finalization(); System.out.println("First print:" + f); f = null; System.gc(); try {// 休息一段时间,让上面的垃圾回收线程执行完成 Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Second print:" + f); System.out.println(f.finalization); } }
四种引用状态
-
强引用(Strong Reference)
- 代码中普遍存在的类似"Object obj = new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
- 抛出OutOfMemoryError终止程序也不会回收具有强引用的对象;通过将对象设置为null来弱化引用,使其被回收
-
软引用(Soft reference)
-
**描述有些还有用但并非必需的对象。在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行二次回收。**如果这次回收还没有足够的内存,才会抛出内存溢出异常。
-
可以用来实现高速缓存
String str = new String("abc") // 强引用 SoftReference<String> softRef = new SoftReference<String>(str); // 软引用
-
-
弱引用(Weak Reference)
-
描述非必需对象。被弱引用关联的对象只能生存到下一次垃圾回收之前,垃圾收集器工作之后,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
String str = new String("abc") // 强引用 WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用
-
-
虚引用(Phantom Reference)
-
不会决定对象的生命周期,和其生存时间完全没关系。
-
任何时候都可能被垃圾收集器回收
-
跟踪对象被垃圾收集器回收的活动,起哨兵作用
-
必须和引用队列ReferenceQueue联合使用
String str = new String("abc") // 强引用 ReferenceQueue queue = new ReferenceQueue(); PhantomReference<String> softRef = new PhantomReference<String>(str, queue); // 虚引用
-
引用类型 关系 被垃圾回收时间 用途 生存时间 强引用 有用必须对象 从来不会 对象的一般状态 JVM停止运行时终止 软引用 有用非必须对象 在内存不足时 对象缓存 内存不足(溢出)时终止 弱引用 非必须对象 在垃圾回收时 对象缓存 gc运行后终止 虚引用 无用对象 Unknown 标记、哨兵 垃圾收集时会收到系统通知
-
-
-
引用队列(ReferenceQueue)
- 无实际存储结构,存储逻辑依赖于内部节点之间的关系来表达
- 存储关联的且被GC的软引用,弱引用以及虚引用