通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,或者从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的
在java技术体系中,固定可作为GC Roots的对象包括以下几种:
1.在虚拟机栈中引用的对象(正在运行的方法所使用到的参数、局部变量、临时变量等)
2.在方法区中类静态属性引用的对象(java类的引用类型静态变量)
3.在方法区中常量引用的对象(字符串常量池里的引用)
4.在本地方法栈中引用的对象
5.java虚拟机内部的引用,基本数据类型对应的Class对象,一些常驻的异常对象等还有系统类加载器。
6.所有被同步锁(synchronized关键字)持有的对象
7.反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
四种引用类型强引用:最传统的“引用”定义,在程序代码中普遍存在的引用赋值。无论任何情况下只要强引用还在,垃圾收集器就永远不会回收掉被引用的对象
软引用:描述一些还有用,但非必须的对象。在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中。SoftReference类来实现。
弱引用:描述那些非必须对象,只能生存到下次垃圾收集发生为止。WeakReference类来实现。
虚引用:为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。PhantomReference类来实现虚引用。
拯救自己当可达性分析算法中判定为不可达的对象,这时候暂时处于“缓刑”阶段,要真正宣告一个对象死亡,最多会经历两次标记过程,如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记。随后进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法。如果没有覆盖此方法或者已经被调用过一次那么就不需要执行了。
回收方法区常量的条件比较简单,假如“java”字符串进入常量池,当系统又没有任何一个字符串对象的值是“java”。这个“java”常量就会被系统清理出常量池。常量池中其他类、方法、字段的符号引用也一样。
要判断一个类型是否属于“不再使用的类”的条件比较苛刻:
该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
垃圾回收算法 分代收集理论当前商业虚拟机的垃圾收集器大多数都遵循了“分代收集”,两个分代假说:
1.弱分代假说:绝大多数对象都是朝生夕灭的。
2.强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。
因为存在老年代引用新生代区域,遍历整个老年代所有对象的方案虽然理论可行但会给内存回收带来很大的性能负担。为了解决这个问题就对分代理论添加第三条:
跨代引用假说:跨代引用相对于同代引用来说仅占极少数
标记-清除算法标记出所有需要回收的对象,标记完成后统一回收掉所有被标记的对象,也可以反过来标记存活的对象,回收未被标记的
缺点:执行效率不稳定,要进行大量的标记和清除动作,内存空间的碎片化
标记-复制算法将内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把这块需要清理的内存一次清理掉。缺点:一半的空间被浪费了
标记-整理算法主要针对老年代,老年代的存活对象一般比较多,使用标记复制算法不仅复制的效率会低而且还要浪费空间,但是标记清除算法会产生大量的内存碎片。标记整理则是不直接清除对象,让所有的存活的对象都向空间一端移动,然后直接清除掉边界以外的。这也会发生常说的“stop the world”
HotSpot算法实现 GCRoot节点枚举由于Java对象太多,在进行根节点枚举的时候,需要遍历找到所有对象显然不太现实(GCRoot的枚举在收集器算法当中一般都需要Stop The World)在HopSpot解决方案当中,采用的是成为OopMap的数据结构,避免扫描所有对象。 JVM可以做到在类加载完成后,就能获取对象的数据信息。将这些信息放到OopMap中,避免了扫描整个程序上下文。
安全点