Java7内存运行区域中,程序计数器、虚拟就栈、本地方法栈是属于线程私有,故在方法或线程结束的时候,内存也就跟着回收了。垃圾收集(Garbage Collection,GC)器主要关注的Java堆及方法区的内存,这部分的内存是动态分配和回收的,只有在程序运行期间才会知道创建了哪些对象。
一、对象是否存活
1.引用计数法
简述:给每个对象一个引用计数器,每当有地方引用时就加1 ,当引用失效时减1,当计数器为0时代表不再使用。
优点:实现简单,判定效率高
缺点:无法解决对象间的循环引用
2.可达性分析算法
简述:选取一系列“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
GC Roots对象:
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
其中栈中的对象不受GC管理,其生命周期跟着线程走,不存在垃圾回收的说法,同时方法区中的引用对象一般为不可变的或持久的,一般不依赖于某个具体的对象实例。故选取这些对象作为GC Root。
3.引用的分类
强引用:new出来的对象,强引用存在则垃圾收集器永远不会回收
软引用:还有用但非必须的对象,内存溢出异常之前进行回收
弱引用:非必须对象,生存到下一次垃圾回收之前
虚引用:完全无影响,在回收时,会得到一个系统通知
4.对象已死?
判断一个对象已死需要经过两次标记过程:
- 如果对象在进行可达性分析后发现没有和GC Roots相连接的引用链,那它会进行第一次标记。
- 判断此对象是否已经执行过finalize()方法,若存在finalize()方法,并且未执行过,则将此对象放置进F-Queue的队列中,之后由一个虚拟机自动建立的低优先级finalizer线程去执行,即会触发finalize()方法,但不保证等待其执行完,避免永久处于等待,导致内存回收系统崩溃。若对象在执行finalize()时与引用链上任何一个对象建立关联,则自救成功,第二次标记时移除回收集合。
二、垃圾收集算法
1.标记-清除算法
步骤:
- 标记所有需要回收的对象
- 统一回收被标记的对象
不足:
- 标记和清除的效率不高
- 标记清除后会产生大量不连续的空间碎片,如下图所示。分配较大对象时,会无法获得较大的空间而触发垃圾收集动作.
2.复制算法
将内存按容量划分为两块,每次只使用其中的一块。当一块的内存用完后,就将还存活的对象复制到另一块上,然后将已使用的内存空间一次清空。主要用于回收新生代。
优点:顺序分配内存空间,实现简单,运行高效
缺点:内存缩小
3.标记整理算法
步骤:
- 标记所有需要回收的对象
- 让所有存活的对象都向一端移动,然后清理掉端边界以外的内存
4.分代收集算法
将Java堆分为新生代和老年代,这样根据各个年代的特点采用最合适的收集算法。新生代使用复制算法,老年代使用标记整理或者标记清除算法。
三、HotSpot的垃圾收集器
1. Serial收集器
Serial收集器是一个单线程的收集器,在进行垃圾收集时,会停止其他所有的工作线程,在JDK1.3.1之前,是虚拟机新生代垃圾收集器的唯一选择。
优点:简单高效
缺点:会停止所有工作线程
适用:运行在Client模式下的虚拟机
2. ParNew收集器
ParNew收集器是Serial收集器的多线程版本。
优点:多线程
缺点:会停止所有工作线程
适用范围:多CPU的环境下与CMS配合使用
3. Parallel Scavenge收集器
Parallel Scavenge收集器更关注达到一个可控制的吞吐量,更加高效的完成利用CPU时间,尽快的完成程序的运算任务。适用于后台运算而不需要太多交互的任务。
提供了两个精确控制吞吐量的参数:
- MaxGCPauseMillis:大于0的毫秒值,收集器尽可能保证内存回收花费的时间不超过设定值。
- GCTimeRatio:大于0小于100的整数,默认值为99。垃圾收集时间占总时间的比率。默认为99代表,允许1/(1+99)即1%的垃圾收集时间。
4. Serial Old收集器
Serial收集器的老年代版本。
5. Parallel Old收集器
Parallel Scavenge收集器的老年代版本。
6. CMS收集器
CMS收集器是以获取最短停顿时间为目标的收集器,基于标记清除算法实现。
运作过程:
- 初始标记:标记GC Roots能直接关联到的对象,需要Stop World
- 并发标记:进行GC RootsTracing的过程,无需Stop World
- 重新标记:修正并发标记时由于程序运作导致标记变动的一部分对象的标记记录,需要Stop World
- 并发清除
缺点:
- CPU资源敏感。默认启动(CPU数量+3)/4个线程进行回收。
- 无法处理浮动垃圾(在标记过程之后程序运行产生的垃圾)
- 收集结束有大量的空间碎片
7. G1收集器
G1是面向服务端应用的垃圾收集器。
特点:
- 并行与并发
- 分代收集
- 空间整理
- 可预测的停顿
运行步骤:
- 初始标记
- 并发标记
- 最终标记
- 筛选回收:根据Region的回收价值和成本进行排序,制定回收计划
四、内存分配策略
- 对象优先在新生代Eden区分配
- 大对象直接进入老年代
- 长期存活(默认15次)的对象进入老年代
- 动态对象年龄判定:如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代
- 空间分配担保:在发生Minor GC前,虚拟机会检查老年代最大可用的连续可用空间是否大于新生代所有对象总空间,如果条件成立,则是Minor GC是安全的,若不成立,会检查是否允许担保失败。如果允许,会检查老年代最大可用的连续空间是否大于历次晋升到老年代的平均大小,如果大于,尝试进行一次Minor GC,否则进行一次Full GC