- Java自动内存管理的核心功能就是 堆 内存中的对象的分配和回收
- 垃圾收集器管理的主要区域就是 堆 ,因此也叫 GC堆
- 垃圾收集器目前基本采用的是分代收集算法
- JDK1.8之后,永久代被元空间代替,使用的直接内存,堆中就剩下新生代和老年代
- 新生代分为Eden、两个S区

内存分配和回收原则
对象优先在Eden区进行分配
- 大多数情况下,对象在新生代的Eden区进行分配
- 如果没有足够的空间,则触发一次MinorGC,存活的对象进行from区,对象年龄加1,此时如果大对象无法放入from,则直接进入老年代
- 当eden区满了后,再次触发MinroGC,Eden区存活对象进入from区,并且from区采用标记复制法移动到to区,对象年龄加1,然后to区和from区名字交换。(即哪个区空着哪个区叫to区)
大对象直接进入老年代
- 大对象就是需要连续内存空间的对象,比如字符串、数组;
- 直接进入老年代也是避免在年轻代是放不下还要复制进老年代
长期存活的对象将进入老年代
- 虚拟机为每个对象一个年龄计数器
- 对象在Eden区分配出生,经过一次年轻代GC后如果还存活,就移动到from区,年龄加1
- 对象在存活区每经过一次年轻代GC,年龄就加1,当年龄增加到一定程度(默认15岁),就会放入老年代中
总结
- 新生代GC:只对新声嗲进行垃圾回收
- fullGC:手机整个Java堆
死亡对象的判断
- 已经死亡的对象才能被回收
引用技术法
- 给对象添加一个引用计数器
- 有一个引用就加1
- 引用失效就减1
- 计数器为0的可以认为是死亡对象
循环引用问题
- 两个对象间相互引用,会导致两个对象的计数器都不是0
- 两个对象都没有其他引用,实际为死亡对象。
此种情况无法通过垃圾回收器进行回收
可达性分析算法
- 以“GC-Roots”的对象为起点
- 从这些节点向下搜索,节点走过的路径叫引用链
- 当一个对象到GC Roots没有任何引用链时,对象不可达
哪些对象可以作为GC Roots
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁持有的对象
对象可以回收,代表对象一定会被回收吗?
- 不可达对象也需要进行两次标记
- 不可达时进行第一次标记,并筛选是否有必要执行
finalize方法 - 需要执行的进行第二次标记,进而被回收
引用类型的分类
不管是哪种判断对象死亡的方法,都需要判断对象是否存在引用。
Java中分为四种引用,依次是
- 强引用
- 软引用
- 弱引用
- 虚引用
强引用
- 最普遍的引用
- 强引用绝对不会被回收
- 当内存不足时,抛出OOM错误,也不会进行回收
软引用
- 软引用可有可无
- 当内存足够时,不会回收
- 内存不够时,就会回收
- 软引用可以实现内存敏感的高速缓存
弱引用
- 弱引用可有可无
- 无论内存是否足够,只要发生GC,就会回收
虚引用
- 任何时候都可能被垃圾回收
总结
- 实际使用过程中,很少会用到弱引用和虚引用
- 软引用可以加速JVM堆垃圾内存的回收速度,防止OOM
如何判断一个常量时废弃的
如果字符串常量池中的一个字符串没有任何对象引用,就是废弃的字符常量
垃圾回收算法
标记-清除
- 第一轮统一进行标记,标记出所有不需要进行回收的对象
- 标记完成后,统一回收掉没有标记的对象
问题
- 效率低
- 产生空间碎片
标记-复制
- 将内存等分位两块,每次使用其中一块
- 一块内存使用完之后,将存活对象复制到另一块
- 复制完成后,将当前块的清理掉
问题
- 可用内存减半
标记-整理
- 首先进行存活对象的标记
- 将所有的存活对象向同一段进行移动
- 移动完成后,清理掉边界之外的所有内存
分代收集
- 根据不同代的特点进行不同的收集算法
- 新生代对象的出生死亡频繁,可以选择标记复制
- 老年代对象存活几率高,可以采用标记整理
垃圾收集器
Serial收集器(串行收集器)
- 单线程
- 垃圾回收的同时必须暂停其他线程,直到回收结束
- 新声代标记复制,老年代标记整理
- 特点:简单高效
ParNew收集器
- 串行收集器的多线程版本
- 多线程并发,应用暂停
Parallel Scavenge收集器
- 可并发收集器几乎一样
- 并行收集器更关注吞吐量,提供参数可以调整最大吞吐量和用户线程的停顿时间
- JDK1.8的默认收集器
CMS收集器
- CMS收集器时一种以获取最短回收停顿时间为目标的收集器
- 并发收集器,可以实现垃圾回收和用户线程基本同时工作
- 采用标记清除算法
执行过程
- 初始标记:暂停其他所有线程,记录下与root直接相连的 对象
- 并发标记:垃圾回收和用户线程同时进行,采用一个闭包结构存储可达对象,记录引用更新。
- 重新标记:暂停用户线程,并修正标记期间发生变化的对象的标记记录
- 并发清除:开启用户线程,同时进行垃圾回收
优缺点
优点:
- 并发收集
- 停顿时间短
缺点:
- CPU资源敏感
- 无法处理浮动垃圾
- 标记清除会产生空间碎片