垃圾回收的区域:堆
识别垃圾
引用计数
- 对象被引用一次,计数加一;当引用为0时,则可以回收
- 缺点:无法解决循环依赖
可达性算法
- 从GC Root对象出发,依次引入其他结点。遍历结束之后,不在任意一条GC root链条上的其他结点,被判为垃圾,可以进行回收
- GC Root对象
- 虚拟机栈中引用的对象
- 方法区中类静态属性/常量引用的对象
- 本地方法栈中 JNI引用的对象
若被标记为垃圾,则一定会被回收吗?
- 不一定。GC时,先判断对象是否执行了 finalize(),如果未执行,则执行 finalize(),之后GC 会再次判断对象是否可达,如果不可达,则会被回收,如果可达,则不回收。
- finalize()只会被执行一次。也就是说,如果第一次执行 finalize()对象变成了可达,则不回收,但如果对象再次被 GC,则对象一定会被回收。
垃圾回收主要方式
- 标记清除
- 根据可达性分析,标记出垃圾,然后回收
- 缺点:由于不移动对象,会造成内存碎片
- 复制算法
- 先内存先分为A、B两块,先将对象都放在A,GC时,将存活的对象移动到B,然后释放A
- 缺点:只有一般内存得到的利用
- 标记整理
- 将存活的对象都往一端移动
- 频繁移动存活对象
- 分代收集
- 根据对象存活周期的不同将堆分成新生代和老生代(1:2)
- 大部分对象在很短的时间内会被回收,因此对象一般分配在 Eden 区
- 经过 Minor GC 后只有少部分对象会存活,它们会被移到 S0 区,同时对象年龄加一
- 把 Eden 区对象全部清理以释放出空间
- 当触发下一次 Minor GC 时,会把 Eden 区的存活对象和 S0(或S1) 中的存活对象一起移到 S1(Eden 和 S0 的存活对象年龄+1), 同时清空 Eden 和 S0 的空间。
- 依次重复(S0和S1互换)
- 根据对象存活周期的不同将堆分成新生代和老生代(1:2)
什么时候对象进入老年代?
- YGC时,To Survivor区不足以存放存活的对象,对象会直接进入到老年代。
- 当对象的年龄达到了我们设定的阈值,则会从S0(或S1)晋升到老年代
- S0(或S1) 区相同年龄的对象大小之和大于 S0(或S1)空间一半以上时,则年龄大于等于该年龄的对象也会晋升到老年代。
- 大对象需要大量的连续内存时,会直接分配在老年代。