JVM垃圾回收
- 垃圾回收就是回收那些不再使用的对象,即释放它们占用的内存。垃圾收集器跟踪所有正在使用的对象,并把其余部分当做垃圾。
一、JVM垃圾回收中的基本概念
| 概念 | 解释 | 说明 |
| Minor GC | 小型GC | 新生代GC,即发生在新生代的垃圾回收动作 |
| Major GC | 大型GC | 老年代GC,指发生在老年代的GC。CMS收集器中,当老年代满时会触发 Major GC。除了CMS收集器,其他都存在都不存在只针对年老代的收集 |
| Full GC | 完全GC | 对整个堆(新生代、年老代)和方法区进行垃圾收集 |
二、常见堆分类介绍
- 研究发现,程序中的大多数可回收的内存可归为两类:
(1)大部分对象很快就不再使用。
(2)还有一部分对象不会立即无用,但也不会持续(太)长时间。
基于这一假设,VM中的内存被分为年轻代(Young Generation)和年老代(Old Generation)。年老代有时候也称为年老区(Tenured)。
拆分成这样两个可清理的单独区域,允许采用不同的算法来大幅提高GC的性能。但这种方法也是有问题的,比如在不同分代中的对象可能会互相引用,在收集某一个分代时就会成为“事实上的”GC root。
三、堆内存池中的内存池划分。(注:不同的GC算法在实现细节上可能会有所不同)

(1)Eden(伊甸园、新生代,在年轻代中(Young Generation)):Eden是内存中的一个区域,用来分配新创建的对象。
通常我们会有多个线程创建多个对象,所以Eden区被划分为多个线程本地分配缓冲区(TLAB)。
通过这种缓冲区划分,大部分对象直接由JVM在对应线程的TLAB中分配,避免与其他线程的同步操作。
如果TLAB中没有足够的内存空间,就会在共享Eden区(Shared Eden space)之中分配。如果共享Eden区也没有足够的空间,就会触发一次年轻代GC来释放空间。
如果GC之后Eden区依然没有足够的空闲内存区域,则对象就会被分配到老年代空间(Old Generation)。

(2)Survivor Spaces(存活区,在年轻代中(Young Generation)):年轻代中所有存活对象都会被复制到“to”存活区,"to"区有对象,“from”区没有对象。
任意时刻总有一个存活区是空的,存活的对象会在两个存活区之间复制,复制到另一个存活区之后,原本的存活区就会被清空。直到某些对象的存活时间达到一定的阈值,就会迁移到年老代。

(3)Old Generation(老年代,也称为Tenured):年老代中的对象大部分都是存活的,年老代GC发生的频率比年轻代小很多,采用移动对象的方式来实现最小化内存碎片。
(4)PermGen(永久代):在Java 8之前有一个特殊的空间,被称为永生代。这是存储元数据(metadata)的地方,比如class信息。
实际上这块区域给java开发者造成了很多麻烦,因为很难去计算这块区域到底占用了多少内存空间。预测失败的结果就是导致产生永生代内存溢出。
(5)元数据区(Metaspace):既然计算元数据区所需的空间那么复杂,java8直接删除了永生代,改用Metaspace。此后,java中很多杂七杂八的东西都放置到普通的堆内存中。
元数据区位于本地内存(native memory),不再影响到普通的java对象。
默认情况下,Metaspace的大小只受限于java进程中可用的本地内存。
这样程序就不再因为多加载了几个类/JAR包就导致永生代内存溢出。
但如果Metaspace失控,则可能会导致很严重的内存交换(swapping),导致本地内存分配失败,可通过限制Metaspace大小的方式处理。
备注:
1)发生fullGC怎么排查
1-1)确认Full GC:查看 GC 日志
在 JVM 启动参数中启用 GC 日志,直接观察 Full GC 的发生频率和耗时:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
日志中的 Full GC 关键字标记了 Full GC 事件,例如:
[Full GC (Ergonomics) ... [PSOldGen: 1024000K->1023999K(1048576K)] ... , secs]
关注 Full GC 前后的老年代内存变化(如 PSOldGen)、耗时(secs)和触发原因(如 Ergonomics、System.gc())。
2)分析 Full GC 的可能原因:定位内存泄漏、配置问题或代码缺陷。
使用Arthas:动态跟踪 JVM 状态,执行命令如 heapdump、vmtool 快速诊断问题。
2-1)内存泄漏(Memory Leak)
-
现象:老年代内存使用率持续增长,Full GC 后内存回收不足,最终导致
OutOfMemoryError。
2-2)系统配置不合理
-
现象:堆内存分配过小或 Survivor 区比例不当,导致对象过早晋升到老年代。
2-3)大对象分配(Large Object Allocation)
-
现象:大对象(如大数组)直接进入老年代,导致老年代快速填满。
3)优化调整:调整 JVM 参数、优化代码逻辑或更换垃圾回收器。
3-1)调整 JVM 参数:
- 增大堆内存(
-Xmx、-Xms)。 -
优化新生代与老年代比例(
-XX:NewRatio)。 -
启用并行 Full GC(
-XX:+UseParallelOldGC)或 G1 GC(-XX:+UseG1GC)。
3-2)代码优化:
-
避免内存泄漏(如未关闭的资源、静态集合缓存)。
-
减少大对象分配(如分页加载数据)。
-
使用软引用(
SoftReference)或弱引用(WeakReference)管理缓存。
3-3)使用 G1 垃圾回收器:
-
G1 通过分区域回收减少 Full GC 的发生:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
更多java基础总结(适合于java基础学习、java面试常规题):
总结篇(9)---字符串及基本类 (1)字符串及基本类之基本数据类型
总结篇(10)---字符串及基本类 (2)字符串及基本类之java中公共方法及操作
总结篇(12)---字符串及基本类 (4)Integer对象
总结篇(14)---JVM(java虚拟机) (1)JVM虚拟机概括
总结篇(15)---JVM(java虚拟机) (2)类加载器
总结篇(16)---JVM(java虚拟机) (3)运行时数据区
总结篇(17)---JVM(java虚拟机) (4)垃圾回收
总结篇(18)---JVM(java虚拟机) (5)垃圾回收算法
总结篇(19)---JVM(java虚拟机) (6)JVM调优
总结篇(24)---Java线程及其相关(2)多线程及其问题
总结篇(25)---Java线程及其相关(3)线程池及其问题
总结篇(26)---Java线程及其相关(4)ThreadLocal
总结篇(27)---Java并发及锁(1)Synchronized
总结篇(31)---JUC工具类(1)CountDownLatch
本文详细介绍了JVM垃圾回收的基本概念,包括Minor GC、Major GC和Full GC的区别,以及年轻代(Young Generation)和年老代(Old Generation)的划分原理。同时,深入探讨了Eden区、Survivor Spaces和Old Generation等内存区域的具体作用。
416

被折叠的 条评论
为什么被折叠?



