在堆里面存放着Java世界中几乎全部的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要肯定这些对象哪些应该被回收。java
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任什么时候刻计数器为零的对象就是不可能再被使用的。web
存在的问题:很难解决对象之间循环引用的问题。算法
主流的Java虚拟机都没有选择该方式来管理内存。缓存
基本思路:经过一系列称为“GC Roots”的根对象做为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过 程所走过的路径称为“引用链”,若是某个对象到GC Roots间没有任何引用链相连, 或者用图论的话来讲就是从GC Roots到这个对象不可达时,则证实此对象是不可能再被使用的。以下图多线程
图片来源:《深刻理解Java虚拟机》并发
固定可做为GC Roots的对象包括如下几种:编辑器
在JDK 1.2版以后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软 引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强 度依次逐渐减弱。ide
若是对象在进行可达性分析后发现没有与GC Roots相链接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,那么虚拟机将这两种状况都视为“没有必要执行”。post
对象能够在finalize()方法中经过将本身与引用链上的对象从新关联,来逃脱垃圾回收。测试
PS:至今没写过这个方法。不推荐使用。
/** * 此代码演示了两点: * 1.对象能够在被GC时自我拯救。 * 2.这种自救的机会只有一次,由于一个对象的finalize()方法最多只会被系统自动调用一次 * * 代码来源:《深刻理解Java虚拟机》 * @author : fuyuaaa * @date : 2020-06-03 15:58 */ public class FinalizeEscapeGC { public static FinalizeEscapeGC SAVE_HOOK = null; public void isAlive() { System.out.println("yes, i am still alive :)"); } @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize method executed!"); FinalizeEscapeGC.SAVE_HOOK = this; } public static void main(String[] args) throws Throwable { SAVE_HOOK = new FinalizeEscapeGC(); //对象第一次成功拯救本身 SAVE_HOOK = null; System.gc(); // 由于Finalizer方法优先级很低,暂停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } // 下面这段代码与上面的彻底相同,可是此次自救却失败了,由于finalize只会被执行一次 SAVE_HOOK = null; System.gc(); // 由于Finalizer方法优先级很低,暂停0.5秒,以等待它 Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isAlive(); } else { System.out.println("no, i am dead :("); } } } result: finalize method executed! yes, i am still alive :) no, i am dead :( 复制代码
方法区的垃圾收集主要回收两部份内容:
废弃的常量:没有地方引用这个常量
再也不使用的类型:(同时知足三个条件)
首先标记出全部须要回收的对象,在标记完成后,统一回收掉全部被标记的对象;也能够反过来,标记存活的对象,统一回收全部未被标记的对象。
缺点:
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,而后再把已使用过的内存空间一次清理掉。
缺点:
如今的商用Java虚拟机大多都优先采用了这种收集算法去回收新生代。
标记过程仍然与“标记-清除”算法同样,但后续步骤不是直接对可回收对象进行清理,而是让全部存活的对象都向内存空间一端移动,而后直接清理掉边界之外的内存。
缺点:
PS:
上图为HotSpot虚拟机的。有连线表明能够搭配使用。
因为维护和兼容性测试的成本,在JDK 8时将Serial+CMS、 ParNew+Serial Old这两个组合声明为废弃(JEP 173),并在JDK 9中彻底取消了这些组合的支持(JEP 214)。
并行(Parallel):并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在协同工做,一般默认此时用户线程是处于等待状态。
并发(Concurrent):并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾收集器线程与用户线程都在运行。因为用户线程并未被冻结,因此程序仍然能响应服务请求,但因为垃圾收集器线程占用了一部分系统资源,此时应用程序的处理的吞吐量将受到必定影响。
具体步骤:
G1把连续的Java堆划分为多个大小相等的独立区域(Region),每个Region均可以扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器可以对扮演不一样角色的 Region采用不一样的策略去处理。
Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1认为只要大小超过了一个 Region容量一半的对象便可断定为大对象。
可预测的停顿时间模型:将Region做为单次回收的最小单元,让G1收集器去跟踪各个Region里面的垃圾堆积的“价值”大小,价值即回收所得到的空间大小以及回收所需时间的经验值,而后在后台维护一个优先级列表,每次根据用户设定容许的收集停顿时间,优先处理回收价值收益最大的那些Region,这也就是“Garbage First”名字的由来。
具体步骤:
PS:
图片来源《深刻理解Java虚拟机》
本文使用 mdnice 排版