哪些内存需要回收
如何判定哪些对象需要回收?在堆里面存放了几乎所有的对象实例,在GC之前第一件事就是要确定这些对象哪些还“存活”着,哪些已经“死去”。其中判断对象是否存活有两种算法:可达性分析算法、引用计数算法。
可达性分析算法
通过一系列的**“GC Roots”根对象作为始节点,开始往下遍历有引用**关系的对象形成一条“引用链”,通过这条引用链的可达的对象是存活的,不可达的对象则是不再使用。
那么什么样的对象可以GC Roots呢?主要是以下对象:
- 虚拟机栈中的引用对象
- 方法区的类静态的引用对象、常量的引用对象
- Java虚拟机内部引用:基本数据类型的Class对象、异常对象、系统类加载器
- 所有同步锁持有的对象
- 反映虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存。
可达性分析算法是大多数系统使用的对象存活判断算法。
引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它,计数器就+1,当一个引用失效,计数器就-1,当计数器>0时就表示对象为存活使用状态,不得回收。大多数不会用这个办法来判断对象存活,因为当对象实例相互引用时,当栈中的引用已经失效,对象也还是不能回收。
上面的可达性分析算法和引用计数算法都用到了引用
,这种引用默认是强引用。在JDK1.2版本之后,Java除了有强引用外,还增加了三种引用:
- 软引用(SoftReference): 内存溢出前,可对持有该引用的对象回收
- 弱引用(WeakReference):GC就会被回收,相当于没有引用。
- 虚引用(PhantomReference): 无法通过其获得对象实例,唯一目的是当对象被回收时能收到一个系统通知。
如果对象被可达性分析算法
或引用计数算法
识别为无使用对象就可以被GC回收吗?不是,一个对象要至少要经过两个的标志过程。其还会查询对象是否重写了finalize()
方法,如果重写了就会用另一个Finalize线程去执行这个finalize方法,这个方法可以时对象重新被引用,但官方并不推荐这样做。
堆中的对象实例可以使用可达性分析算法和引用计数算法来决定是否回收。但方法区的常量池和类型信息是否回收却另有条件:
常量池的字符串和符号引用 虚拟机中没有任何对象有引用到它。
类型卸载 1.该类的所有实例被回收 2.该类的ClassLoader被回收 3.Class对象没有被任何地方引用 以上三种条件同时被满足。