前言
JAVA 虚拟机的垃圾收集器是虚拟机内存的清道夫, 它的存在让 JAVA 开发人员能将更多精力投入到业务研发上. 了解垃圾收集器, 并利用好这个工具, 能更好的保障服务稳定性. 这篇文章通过分析 JAVA 虚拟机内存模型, 介绍垃圾收集器常用算法和收集器类别, 使得垃圾收集器的配置和使用变得不再遥不可及.
JAVA 虚拟机内存模型
JAVA 虚拟机内存可以划分为: 虚拟机栈, 本地方法栈, JAVA 堆内存, 方法区 (包含运行时常量池), 程序计数器, 直接内存.
虚拟机栈
虚拟机栈是线程私有的, 生命周期跟线程相同. 也就是说一个线程被创建后, 虚拟机为其分配了一个独立的栈帧来存储线程的局部变量, 操作数, 动态链接, 方法出口等信息, 当线程结束后, 该栈帧也会被回收清理.
本地方法栈
本地方法栈是虚拟机的 native 方法执行期间使用的一个栈帧.
JAVA 堆内存
堆内存是被所有线程共享的一块区域, 用来存放对象实例和数组, 属于内存中最大的一块区域, 也是垃圾收集的主要区域. 从垃圾收集的角度看, 堆内存经常分为新生代和老年代.
方法区
方法区也是被所有线程共享的一块区域, 用来存储被虚拟机加载的类信息, 常量, 静态变量, JIT 编译后代码等数据. 也可以成为永久代.
程序计数器
程序计数器是线程私有的, 作为当前线程所执行的字节码的行号指示器, 每个线程有一个程序计数器, 用于记录 CPU 切换线程时记录当前线程的执行位置, 以便下次继续从当前位置往下执行.
直接内存
这块不属于 JAVA 虚拟机内存, 但使用频繁, 也可称之为 "堆外内存"
JAVA 虚拟机垃圾收集器
根据上述对 JAVA 虚拟机内存区域模型的介绍, 我们知道 JAVA 程序中的对象实例都存储在 JAVA 堆内存中, 因此垃圾收集主要也是针对堆内存进行. 为了更好的管理 JAVA 对象实例, 并结合对象实例的生存时间长短, JAVA 虚拟机将堆内存分为新生代和老年代, 分别存储刚创建不久的对象和存活较长时间的对象实例, 并采用分代收集的策略分别回收新生代和老年代的内存.
内存分配与回收策略
1, 分代收集思路. 根据 JAVA 对象的生存周期特点, 虚拟机将堆内存分为新生代和老年代, 并分别采用新生代和老年代的垃圾回收策略.
2, 新生代细分为 Eden 区和两个 Survivor 区 (即 From 区和 To 区). 大多数新生对象创建频繁, 且存活时间短, 为了提高新生代区域垃圾收集效率, 新创建的对象存放在 Eden 区, 当 Eden 区快满的时候, 虚拟机对其触发一次 Minor GC, 将新生代存活对象移动到 From 区, 原来 From 区的对象根据存活年龄决定放到 To 区还是老年代, 然后清空 Eden 区和 From 区, 接着将 To 区对象全部移到 From 区.
3, 大对象直接进入老年代, 可以配置新生代对象的最大值, 对象超过这个值就直接进入老年代.
4, 发起 Minor GC 前, 会先判断老年代最大可用连续空间是否大于新生代对象占用的空间, 如果小于或不允许冒险, 则触发一次 Full GC.
垃圾收集算法 (3 种基本算法)
1, 复制算法. 针对于新生代的垃圾收集算法. 当新生代 Eden 区快满的时候, 将 Eden 区对象复制到 From 区, 将 From 区对象根据存活年龄决定复制到 To 区还是到老年代, 然后清除 Eden 区和 From 区, 接着将 To 区对象复制到 From 区.
2, 标记 - 清除算法. 垃圾收集算法标记出需要回收的对象, 标记完成后直接统一回收. 垃圾收集器使用可达性分析来判断哪些对象是否存活, 通过设置一系列 GC Roots 节点 (包括栈, 方法区中的静态属性和常量所引用的对象, 以及本地方法栈中引用的对象), 从这类节点往下搜索, 当对象不在 GC Root 节点的引用链上时, 说明对象不可达, 可以被回收.
3, 标记 - 整理算法. 垃圾收集算法标记出需要回收的对象, 标记完成后将存活对象往内存的一端移动, 然后直接清理掉端边界以外的内存.
常用垃圾收集器
由于虚拟机中的垃圾收集是分代收集的, 新生代和老年代的垃圾收集策略不太一样, 所以一般是使用针对新生代和老年代的垃圾收集器组合.
1, Serial GC. 新生代收集器, 采用复制算法, 用于 Client 客户端新生代垃圾收集, 针对内存占用较少的应用进行垃圾收集.
2, Serial Old GC. 老年代收集器, 采用标记 - 整理算法, 用于 Client 客户端老年代垃圾收集, 针对内存占用较少的应用进行垃圾收集.
3, Parallel Scavenge GC. 新生代收集器, 采用复制算法, 并行收集新生代内存垃圾, 可以设置垃圾收集器的吞吐量, 还可以设置自动适配调节吞吐量.
4, Parallel New GC. 新生代收集器, 采用复制算法, 并行收集新生代内存垃圾.
5, Parallel Old GC. 老年代收集器, 采用标记 - 整理算法, 并行收集老年代内存垃圾.
6, CMS GC. 老年代收集器, 采用标记 - 清除算法, 并行收集老年代内存垃圾, 不整理内存. 由于在执行垃圾收集期间不中断业务线程, 所以容易产出 "浮动垃圾", 导致 Full GC. 可以通过设置参数来触发内存整理任务.
7, G1 GC. 不再将堆内存区分新生代和老年代, 而是将堆内存看作若干个均分小区域, 并对最空闲的内存区域进行标记和回收. 适用于大内存的应用.
来源: http://www.bubuko.com/infodetail-2599165.html