如何判断对象是否存活?
- 引用计数法
给对象添加一个引用计数器,每当一个地方引用它时,计数器值就加1;
当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的。
很难解决对象间相互循环引用的问题 - 可达性分析算法
通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路称为引用链(reference chain)。当一个对象到GC Roots没有任何引用链相连时,则该对象不可用。
可作为GC Roots的对象包括下面几种
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
引用类型
- 强引用:只要引用还存在,垃圾收集器永远不会回收
- 软引用:在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收
- 弱引用:垃圾收集器工作时就会回收掉
- 虚引用:能在对象被垃圾回收时收到一个系统通知
生成还是死亡
如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选条件是该对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者,finalize()已经被虚拟机调用过,虚拟机将这2种情况视为“没有不要执行”。
如果该对象被判定为有必要执行,那么这个对象就会被放置在一个F-Queue的队列中,并由一个Finalizer线程去执行它
回收方法区
永久代的垃圾收集主要回收两部分:废弃常量和无用类
判定无用类的条件:
- 该类所有实例都已经被回收
- 加载该类的classloader已经被回收
- 该类对应的Class对象没有在任何对象被引用
垃圾收集算法
- 标记-清除算法
标记所有需要回收的对象,标记完成后统一回收所有被标记的对象,效率不高,容易造成内存碎片 - 复制算法
将内存按容量划分为大小相等的2块,每次使用1块,当这块内存用完就将存活的对象复制到另一块。缺点可用内存缩小一半。 - 标记-整理算法
标记过程一样,让所有存活的对象移动到一段,然后直接清理掉其他内存 - 分代收集算法
年轻代:对象存活周期短,采用复制算法
老年代:对象存活率高,采用标记法
枚举根节点
GC停顿(Stop the world):可达性分析工作必须执行在能确保一致性的快照执行中,一致性指整个分析期间整个执行系统看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系发生变化的情况。导致GC进行时必须停止所有JAVA线程
Hotspot中使用一组称为OopMap的数据结构来达到这个目的,在类加载的时候,会记录下来
安全点
hotspot没有为每条指令都生成OopMap,只是在特定的位置记录了这些信息,这些位置称为安全点,程序到达安全点时才能暂停,
安全点的选择标准:是否具有让程序长时间执行的特征,例如方法调用,循环跳转,异常跳转等。
主动式中断:当GC需要中断线程时,不直接对线程操作,仅仅设置一个标志,各线程执行时主动去轮询这个标志,发现中断标志为真时,就自己中断挂起
安全区域
安全区域指一段代码片段之中,已用关系不会发生改变。在这个区域任意地方开始GC都是安全的。
在线程执行到safe region中的代码时,首先标志自己已经进入了sage region,在这段时间里JVM要执行GC时,就不用管标志自己为sage region的状态的线程,在线程要离开safe region时,它要检查GC过程,如果完成了就继续执行,否则它必须等到收到可以安全离开的信号为止。
垃圾收集器
- 并行(Parallel):多条垃圾收集器线程并行工作,但此时用户线程仍然处于等待状态
- 并发(Concurrent):用户线程与垃圾收集线程同时执行(可能是交替),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上
- 新生代收集器:
- Serial收集器:新生代、单线程、Client模式下默认新生代收集器
- 在 ParNew收集器:Serial多线程版,-XX:ParallelGCThread参数限制垃圾搜集的线程数
- parallel Scavenge收集器:新生代、目标是达到一个可控制的吞吐量
- 老年代收集器
- Serial Old:Serial老年代版,单线程,标记-整理
- Parallel Old:parallel Scavenge老年版
- CMS收集器:Concurrent Mark Sweep,一种以获取最短回收停顿时间为目标的收集器,标记-清除算法
4个步骤:
初始标记,并发标记,重新标记,并发清除
3个缺点:
对CPU资源非常敏感、无法处理浮动垃圾、产生内存碎片 - G1收集器:
并发与并行,分代收集,空间整合,可预测的停顿
将JAVA堆内存划分为多个大小相等的独立区域
内存分配与回收策略
- 对象优先分配在Eden区,当Eden区没有足够空间时,JVM发起一次Minor GC.
- 大对象直接进入老年代,避免Eden区以及2个Survivor区之间发生大量的内存复制
- 长期存活的对象将进入老年代,经过多次Minor GC(默认15次)后仍然存活,将会被晋升到老年代
- 动态对象年龄判断,如果在Survivor空间中相同年龄所有对象大小的总和大于Surivivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄
- 空间分配担保,在Minor GC之前,虚拟机先检查老年代最大可用连续空间是否大于新生代所有对象总空间,如果成立,那么Minor GC可以确保是安全的,如果不成立,则检查是否允许担保失败,若果允许,那么继续检查老年代最大可用内存空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,否则进行Full GC
网友评论