Java工程师的你,真的不想了解一下《JVM垃圾回收详解》吗?(重点)
文章目录
- Java工程师的你,真的不想了解一下《JVM垃圾回收详解》吗?(重点)
-
- 前言
- 堆空间的基本结构
- 内存分配和回收原则
-
- 对象优先在 Eden 区分配
- 大对象直接进入老年代
- 长期存活的对象将进入老年代
- 主要进行 gc 的区域
- 空间分配担保
- 死亡对象判断方法
-
- 引用计数法
- 可达性分析算法
- 引用类型总结
- 如何判断一个常量是废弃常量?
- 如何判断一个类是无用的类?
- 垃圾收集算法
-
- 标记-清除算法
- 复制算法
- 标记-整理算法
- 分代收集算法
- 垃圾收集器
-
- Serial 收集器
- ParNew 收集器
- Parallel Scavenge 收集器
- Serial Old 收集器
- Parallel Old 收集器
- CMS 收集器
- G1 收集器
- ZGC 收集器
如果没有特殊说明,都是针对的是 HotSpot 虚拟机。
本文基于《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》进行总结补充。
常见面试题:
- 如何判断对象是否死亡(两种方法)。
- 简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。
- 如何判断一个常量是废弃常量
- 如何判断一个类是无用的类
- 垃圾收集有哪些算法,各自的特点?
- HotSpot 为什么要分为新生代和老年代?
- 常见的垃圾回收器有哪些?
- 介绍一下 CMS,G1 收集器。
- Minor Gc 和 Full GC 有什么不同呢?
前言
当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。
堆空间的基本结构
Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收。
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。
从垃圾回收的角度来说,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆被划分为了几个不同的区域,这样我们就可以根据各个区域的特点选择合适的垃圾收集算法。
在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:
- 新生代内存(Young Generation)
- 老生代(Old Generation)
- 永久代(Permanent Generation)
下图所示的 Eden 区、两个 Survivor 区 S0 和 S1 都属于新生代,中间一层属于老年代,最下面一层属于永久代。
JDK 8 版本之后 PermGen(永久) 已被 Metaspace(元空间) 取代,元空间使用的是直接内存 。
关于堆空间结构更详细的介绍,可以回过头看看 Java 内存区域详解 这篇文章。
内存分配和回收原则
对象优先在 Eden 区分配
大多数情况下,对象在新生代中 Eden 区分配。当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。下面我们来进行实际测试一下。
测试代码:
public class GCTest {
public static void main(String[] args) {
byte[] allocation1, allocation2;
allocation1 = new byte[30900*1024];
}
}
通过以下方式运行:
添加的参数:-XX:+PrintGCDetails
运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代):
从上图我们可以看出 Eden 区内存几乎已经被分配完全(即使程序什么也不做,新生代也会使用 2000 多 k 内存)。
假如我们再为 allocation2
分配内存会出现什么情况呢?
allocation2 = new byte[900*1024];
给 allocation2
分配内存的时候 Eden 区内存几乎已经被分配完了
当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。GC 期间虚拟机又发现 allocation1
无法存入 Survivor 空间,所以只好通过 分配担保机制 把新生代的对象提前转移到老年代中去,老年代上的空间足够存放 allocation1
,所以不会出现 Full GC。执行 Minor GC 后,后面分配的对象如果能够存在 Eden 区的话,还是会在 Eden 区分配内存。可以执行如下代码验证:</