3.2对象已死?(P68)
3.2.1引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值+1,当引用失效时,计数器值-1,为0的对象就是不可能再被使用的
3.2.2 可达性分析算法
通过一系列称为"GC ROOT"的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,走过的路径叫做"引用链(Reference Chain)",如果某个对象到GC Roots间没有任何引用链相连,(不可到达),证明此对象不可再被使用
GC Roots的对象
- 在虚拟机栈(栈中的本地变量表)中引用的对象,如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等
- 在方法区中静态属性引用的对象,如引用类型静态变量
- 在方法区中常量引用的对象,如字符串常量池(String Table)里的引用
- 在本地方法栈中的JNI(即Native方法)引用的对象
- Java虚拟机内部的引用,如基本类型对应的Class对象,一些常驻的异常对象(如NullPointException、OutOfMemoryError),还有系统类加载器
- 所有被同步锁(Synchronized关键字)持有的对象
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
3.2.3 引用细分
JDK1.2后,对引用的概念进行了扩充,将引用分为强引用,软引用,弱引用和虚引用
强引用()
最传统的引用,存在引用就不会被回收,如Object o = new Object();
软引用(SoftReference)
一些还有用,但是非必须的引用,再内存溢出的时候进行第二次回收,这些引用会被回收,第二次回收后还不足,则抛出内存溢出异常
弱引用(WeakReference)
一旦GC就被回收
虚引用(PhantomReference)
虚引用完全不会对对象的生存构成影响,只是在对象被回收的时候会的到一个通知
3.2.4 生存和死亡
在可达性分析中被判定为不可达的对象还有一次自我拯救的机会===>重写finalize方法
finalize方法
- 任何一个对象的finalize()方法最多被调用一次,即最多复活一次
- 没有被引用的对象被第一次标记,随后进行以此筛选,如果该对象没有覆盖,或者已经执行过finalize()方法,则不会再执行
- 判定为可以执行finalize方法的将会被加入一个F-Queue队列,并由虚拟机自动建立一个低优先级的线程执行(注意不保证方法全部执行完,防止有的方法死循环或者太久)
- 只要在方法中重新与其他方法建立引用关系,在待会儿的第二次标记中即可即可移出被回收集合
- 一般不建议使用
3.2.5 回收方法区
方法区的垃圾手机主要回收两部分内容:废弃的常量和不再使用的类型
需要同时满足以下三个条件
- 该类的所有实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例
- 加载该类的类加载器已经被回收,通常很难达成
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
3.3 垃圾收集算法
3.3.1 分代收集收集算法
根据不同的内存区域使用不同的算法
3.3.2 标记-清除算法
主要缺点
- 执行效率不稳定,如果此时大部分堆中的对象需要被回收,则标记和清除的执行效率大大降低
- 产生大量不连续的内存碎片
3.3.3 标记-复制算法
- 一开始是半边复制算法,即会浪费一般的动检
- 后来在新生代回收时使用,分为eden:from:to = 8:1:1
3.3.4 标记-整理算法
主要缺点
- 移动内存太消耗时间
3.4 HotSopt算法细节实现
3.5 经典垃圾收集器
垃圾收集器就是垃圾回收算法的落地实现,默认是并行垃圾回收器
控制台 :java -XX:+PrintCommandLineFlags -version
- Serial
串行垃圾回收器,为单线程环形设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,因此不适合服务器环境 - Parallel
并行垃圾回收器,多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等弱交互场景 - CMS
并发垃圾回收器,用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程,互联网公司多用它,适用对响应时间有要求的场景 - G1
G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收