垃圾回收回收的是什么
-
对象
-
类元数据: 类的元数据包括类的结构信息、方法信息、字段信息等,它们存储在方法区(Metaspace)中。当一个类不再被引用时,垃圾回收器会卸载这个类,并释放其在方法区中占用的内存空间。
-
线程栈和本地方法栈
-
类加载器: 类加载器负责加载类并生成类的元数据。当一个类加载器不再被使用时,垃圾回收器会清理它所加载的类及其相关的元数据,并释放相应的内存空间。
-
其他资源: 包括文件句柄、网络连接、数据库连接等外部资源。这些资源通常由 Java 虚拟机管理器进行管理,垃圾回收器不会直接回收它们,但在某些情况下,这些资源可能会被释放,比如对象的 finalize() 方法被调用时。
判断对象已死
程序计数器,虚拟机栈,本地方法区,随一个线程生,随一个线程死,在编译时就确定了空间大小,不需要判断对象状态。
堆和方法区却拥有很大的不确定性,需要判断其中的对象是否已死。
引用计数法:缺点明显
给每个对象一个引用数,被引用一次+1,取消引用-1。
缺点:当两个对象互相引用时,无法判断已死。
可达性分析算法:主流使用(java,C#)
GC ROOT 对象及其引用的对象被认为是“活动”的,即它们是程序中正在使用的对象,不会被垃圾回收器回收。因此,垃圾回收器会从 GC ROOT 对象出发,沿着对象之间的引用链,标记所有可达的对象,然后清除未被标记的对象,以释放内存空间。
GC ROOT是一定存活的对象
GC ROOT有哪些:
-
虚拟机栈中的引用
-
本地方法栈中的引用
-
方法区中的类静态属性引用的对象: 类的静态属性保存在方法区中,如果一个对象被类的静态属性引用所引用,那么这个对象也是 GC ROOT。
-
方法区中的常量引用的对象: 常量池中的字符串常量、类的静态常量等,如果一个对象被方法区中的常量引用所引用,那么这个对象也是 GC ROOT。
-
JNI(Java Native Interface)中的引用: 本地代码中也可能会引用 Java 对象。如果一个对象被 JNI 中的引用所引用,那么这个对象也是 GC ROOT。
-
synchronized持有的对象。
再谈引用
JDK1.2后对引用进行了扩充,
强引用(Strong Reference): 强引用是最常见的引用类型,它是指通过 new 关键字创建的对象所持有的引用。只要存在强引用指向一个对象,这个对象就不会被垃圾回收器回收,即使内存不足时也不会回收这些对象。当没有强引用指向一个对象时,这个对象就可以被垃圾回收器回收。
软引用(Soft Reference): 软引用是一种比较弱的引用类型,用于描述一些还有用但非必需的对象。当内存不足时,垃圾回收器会回收软引用所引用的对象。软引用通常用于实现缓存等功能,可以避免内存溢出的情况发生。
弱引用(Weak Reference): 弱引用比软引用更弱,它只能保持对一个对象的非必需引用。当对象只被弱引用所引用时,即使内存充足,垃圾回收器也会回收这个对象。弱引用通常用于实现一些特定的功能,比如监视对象的生命周期等。
虚引用(Phantom Reference): 虚引用是一种比较特殊的引用类型,它通常不被用于获取对象的实例,而是用于跟踪对象被垃圾回收器回收的状态。虚引用是一种最弱的引用类型,它不能单独使用,必须和引用队列(ReferenceQueue)一起使用。当对象被回收时,虚引用会被添加到引用队列中,通知程序对象的回收情况。
非死不可与自我拯救
当可达性算法分析一个对象该死亡时,虚拟机会判断这个对象是否调用过了finalize()方法,如果调用过,则判定为没有必要执行垃圾回收。
如果判断为有必要执行finalize()方法,那么这个对象会被放置在一个F-Queue队列中,并且在稍后由一个虚拟机自己建立的,低调度优先级的线程去执行其finalize()方法。
对象可以在finalize()方法中拯救自己,如果此时它和GC ROOT建立了联系,则拯救了自己。
回收方法区的垃圾
在《java虚拟机规范》中提到,方法区可以没有垃圾回收,因为方法区的垃圾回收性价比很低,只有很少的内存可以被回收,确实有些虚拟机也没有实现方法区的垃圾回收。
方法区回收的主要是:废弃的常量和不再使用的类的元数据。
垃圾回收算法
垃圾回收算法主要是:引用式和追踪式,主流的垃圾回收算法使用的都是追踪式,我们讨论的也是追踪式的。