慎看,一些是自己的理解,不能保证正确性,完全是自己学习路上的一个记录
注意点
-
判断对象已死
- 判断对象已死一定和引用相关,这里介绍一下引用和其分类.一般的理解是:引用代表另一块内存的起始地址,这种定义无法描述一些特殊的情况,所以将引用分为四中类型,引用强度逐渐减弱:
- 强引用:强引用就是一般认为的引用,类似
Object o = new Object();
.只要强引用还在,GC就不会回收对象 - 软引用:软引用是用来描述一些还有用但是非必须的对象.可以使用
SoftReference
类来实现软引用.当将要发生内存溢出异常时,将会回收软引用的对象. - 弱引用:也是指非必需对象,但是比软引用还要弱一点,使用
WeakReference
来声明,软引用的对象只能活到下一次GC前,无论内存是否够用,都会被回收. - 虚引用:这个引用对于开发人员没什么意义,为一个对象设置虚引用的目的是为了这个对象被回收时收到一个系统通知,具体怎样不是很了解.使用
PhantomReference
类 - 其实这些我平时都没有接触到,我认为如果不是面试,没必要记住,虽然我们会遇到,但是到时再看也无妨,这些知识一段时间不用很难记得很清楚.记得模糊又没什么用.
- 强引用:强引用就是一般认为的引用,类似
- 引用计数法
- 很容易理解的一种方法,给每个对象添加一个引用计数器,有地方引用就加一,引用失效就减一.
- 缺点:很容易想到,如果两个已经不可能被访问的对象互相持有对方的引用,那么这两个对象的引用计数永远不为0,GC无法回收,造成空间浪费.
- 虚拟机并没有使用这个方法
- 可达性分析算法
- 基本思路是通过一系列的"GC ROOTS"的对象最为起点,从这些节点向下搜索,搜索所走过的路径被称为引用链,当一个对象到根没有任何一条引用链,则证明这个对象已死
- 一般的"GC ROOTS"有如下几种:
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法引用的对象
- 判断对象已死一定和引用相关,这里介绍一下引用和其分类.一般的理解是:引用代表另一块内存的起始地址,这种定义无法描述一些特殊的情况,所以将引用分为四中类型,引用强度逐渐减弱:
-
真正的死亡
- 上面说到判断对象是否已死,但是在实际的虚拟机内,并不是判断已死就会被回收.书上用了一个词,这些对象还是有一个"缓刑"的过程,一个对象的真正的死亡,有两个标记的过程.第一次标记会进行一次筛选,筛选的条件是次对象是否有必要执行
finalize()
方法,两个问题,一:什么是finalize()方法,二:什么是有必要.- finalize方法,就像spring中为bean织入的销毁时运行的方法,但只是意思上相近
- 对象没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,这两种情况被称为没有必要
- 筛选剩下的是有必要执行finalize()方法的对象,进入第二次标记,它们被放在一个叫做
F-Queue
的对列中,稍后会进行第二次标记,如果在对象覆盖重写了finalize方法并且在方法内部重新和根有了引用链,就可以获得重生,否则就会被回收. - 这里的内容其实只是说说,按照作者的意思,完全可以忘却finalize()这个方法,这只是Java诞生初期对于C/C++程序员的妥协.
- 上面说到判断对象是否已死,但是在实际的虚拟机内,并不是判断已死就会被回收.书上用了一个词,这些对象还是有一个"缓刑"的过程,一个对象的真正的死亡,有两个标记的过程.第一次标记会进行一次筛选,筛选的条件是次对象是否有必要执行
-
方法区回收
- 方法区回收的只有两种,一是废弃的常量,这个和堆中的对象一样,当没有对象引用这个字面量,就可以回收.
- 第二个是无用的类,这里的类就是在类加载期间存放在方法区内的描述类的数据结构,判断无用的类有三个条件:
- 所有该类的实例对象已经被回收
- 该类的类加载器被回收(这个不是很理解,如果不使用自定义类加载器,不就没有无用的类了)
- 该类对应的Class对象没有被引用
-
垃圾收集算法
- 标记-清除算法
- 最基础的算法,首先标记处所有需要被回收的对象(前面的判断对象已死),然后标记完成后进行统一回收
- 两个问题,一是效率,标记和清除的效率都不高,另一个是空间问题,大部分情况下,被回收的对象都不是连续在内存空间的,这样会产生很多内存碎片
- 复制算法
- 将内存划分成等大的两块,每次只使用其中一块,满了后,将活着的复制到另一块,然后清除原来的.
- 内存利用率太低,只使用了一半.当对象的存活率较高,效率会很低
- 听起来像是回收新生代的算法,其实就是很多商业虚拟机的收集算法.
- 标记 - 整理 算法
- 框架上类似标记-清除算法,但是在清除阶段,不是直接清理,而是让所有存活的对象移到一边,然后清除掉边界外的内存
- 这是老年代的收集算法.
- 分代收集算法
- 其实就是上面说的,新生代对象存活率低,大多"朝生夕死",所以使用复制算法,老年代存活率高,使用标记-整理算法.
- 标记-清除算法
-
Hotspot垃圾收集实现
- 枚举根节点,前面说到的判断对象已死,其实实现起来要考虑很多事情,一是引用太多,而是为了保持一致性,必须在判断期间停止所有线程(stop the world),主流的虚拟机使用的是准确式GC,即不会一个不漏的检查所有引用,而是使用OopMap的数据结构存放引用的具体位置,使得GC在扫描时直接找到信息.
- 刚才说到的OopMap如果存放所有指令的对象的引用变换,那单单它本身就是一个很大的开销.这里使用的一个叫做"安全点"的东西,记录只会在安全点的情况,而线程也只有到达安全点才能停止.还有安全区的概念,不详细说了.