元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
JVM架构
类加载器机制
运行时数据区
执行引擎工作原理
JIT编译器优化
▶
内存管理
垃圾回收算法
分代收集机制
内存分配策略
内存泄漏诊断
▶
并发编程
线程池实现原理
synchronized锁优化
AQS框架解析
并发集合类原理
▶
字节码技术
字节码指令集解析
ASM操作指南
动态代理实现
字节码验证机制
▶
性能调优
JVM参数解析
GC日志分析
堆内存诊断
JIT性能优化
发布时间:
2025-03-22 09:29
↑
☰
# Java分代收集机制 Java的分代收集机制是JVM进行内存管理的一种重要策略,它基于对象的生命周期特征来进行垃圾回收。本文将详细介绍Java分代收集的原理、实现和优化策略。 ## 分代收集基础 ### 1. 分代假设 分代收集基于两个主要假设: 1. 大多数对象都是短命的(朝生夕死) 2. 存活时间长的对象,未来存活的可能性更大 ### 2. 内存分代 ```java public class GenerationDemo { public static void main(String[] args) { // 新生代对象 for (int i = 0; i < 100; i++) { createTempObject(); } // 可能晋升到老年代的对象 List<Object> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(new byte[1024 * 1024]); // 1MB } } private static void createTempObject() { byte[] temp = new byte[1024]; // 1KB } } ``` JVM堆内存分代: 1. 新生代(Young Generation) - Eden区 - Survivor区(From和To) 2. 老年代(Old Generation) ## 新生代收集 ### 1. Eden区 ```java public class EdenAllocationDemo { private static final int _1KB = 1024; public static void main(String[] args) { // Eden区分配 byte[] allocation1 = new byte[_1KB]; byte[] allocation2 = new byte[_1KB]; byte[] allocation3 = new byte[_1KB]; System.out.println("Eden区分配完成"); } } ``` Eden区特点: - 大多数对象首先在Eden区分配 - 当Eden区满时触发Minor GC - 采用复制算法进行收集 ### 2. Survivor区 ```java public class SurvivorDemo { public static void main(String[] args) { byte[] array1 = new byte[1024 * 1024]; // 1MB // 第一次Minor GC后,存活对象进入From区 byte[] array2 = new byte[1024 * 1024]; // 第二次Minor GC后,存活对象从From区复制到To区 } } ``` Survivor区作用: - 作为Eden区和老年代的缓冲区 - 减少对象直接进入老年代 - 提高对象晋升的门槛 ## 对象晋升 ### 1. 晋升条件 ```java public class PromotionDemo { private static final int _1MB = 1024 * 1024; public static void main(String[] args) { // 大对象直接进入老年代 byte[] largeObject = new byte[4 * _1MB]; // 长期存活的对象 List<byte[]> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { list.add(new byte[_1MB]); System.gc(); // 触发GC,增加对象年龄 } } } ``` 对象晋升的情况: 1. 长期存活的对象 2. 大对象直接进入老年代 3. Survivor区空间不足 4. 动态年龄判断 ### 2. 年龄计数 ```java public class AgeCountDemo { public static void main(String[] args) { // -XX:MaxTenuringThreshold=15 // -XX:+PrintTenuringDistribution byte[] array = new byte[1024 * 1024]; // 1MB for (int i = 0; i < 20; i++) { System.gc(); // 每次GC后对象年龄增加 System.out.println("第" + (i + 1) + "次GC"); } } } ``` 年龄计数机制: - 对象每经过一次Minor GC年龄加1 - 达到阈值后晋升到老年代 - 可通过参数调整阈值 ## 老年代收集 ### 1. 触发条件 ```java public class MajorGCDemo { private static final int _1MB = 1024 * 1024; public static void main(String[] args) { List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[_1MB]); if (list.size() > 1000) { list.clear(); System.gc(); // 可能触发Full GC } } } } ``` 老年代GC触发条件: 1. 老年代空间不足 2. 方法区空间不足 3. 显式调用System.gc() 4. 担保失败 ### 2. 收集算法 ```java public class OldGenCollectionDemo { public static void main(String[] args) { // -XX:+UseSerialGC // -XX:+UseParallelGC // -XX:+UseConcMarkSweepGC // -XX:+UseG1GC List<byte[]> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { list.add(new byte[1024 * 1024]); // 1MB if (i % 10 == 0) { list.subList(0, i/2).clear(); } } } } ``` 常用收集器: 1. Serial Old收集器 2. Parallel Old收集器 3. CMS收集器 4. G1收集器 ## 收集器优化 ### 1. 参数调优 ```java public class GCTuningDemo { public static void main(String[] args) { // JVM参数示例 // -Xmn: 新生代大小 // -XX:SurvivorRatio: Eden和Survivor比例 // -XX:MaxTenuringThreshold: 晋升阈值 // -XX:PretenureSizeThreshold: 大对象阈值 Runtime runtime = Runtime.getRuntime(); System.out.println("最大内存: " + runtime.maxMemory() / 1024 / 1024 + "MB"); System.out.println("总内存: " + runtime.totalMemory() / 1024 / 1024 + "MB"); System.out.println("空闲内存: " + runtime.freeMemory() / 1024 / 1024 + "MB"); } } ``` 重要参数: - -Xmn:新生代大小 - -XX:SurvivorRatio:Eden和Survivor比例 - -XX:MaxTenuringThreshold:晋升阈值 - -XX:PretenureSizeThreshold:大对象阈值 ### 2. GC日志分析 ```java public class GCLogDemo { public static void main(String[] args) { // GC日志参数 // -XX:+PrintGCDetails // -XX:+PrintGCDateStamps // -Xloggc:gc.log List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 1MB if (list.size() > 100) { list.subList(0, 50).clear(); System.gc(); } } } } ``` 日志分析要点: 1. GC类型和时间 2. 内存变化情况 3. GC原因分析 4. 停顿时间统计 ## 最佳实践 ### 1. 内存分配 ```java public class MemoryAllocationDemo { public static void main(String[] args) { // 1. 对象优先在Eden区分配 byte[] edenObject = new byte[1024 * 1024]; // 2. 大对象直接进入老年代 byte[] largeObject = new byte[4 * 1024 * 1024]; // 3. 长期存活对象进入老年代 List<Object> longLivedList = new ArrayList<>(); for (int i = 0; i < 100; i++) { longLivedList.add(new Object()); } } } ``` 分配建议: 1. 避免创建过多短命对象 2. 合理设置大对象阈值 3. 控制对象生命周期 ### 2. 收集器选择 ```java public class CollectorSelectionDemo { public static void main(String[] args) { // 收集器选择考虑因素: // 1. 应用程序的特点 // 2. 系统资源情况 // 3. 停顿时间要求 // 示例:低延迟应用 // -XX:+UseConcMarkSweepGC // -XX:+UseParNewGC // -XX:CMSInitiatingOccupancyFraction=75 // -XX:+UseCMSInitiatingOccupancyOnly performOperation(); } private static void performOperation() { // 模拟应用程序操作 while (true) { // 处理业务逻辑 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 选择建议: 1. 客户端应用:Serial + Serial Old 2. 服务器应用:Parallel Scavenge + Parallel Old 3. 低延迟要求:ParNew + CMS 4. 大内存应用:G1 ## 总结 通过本文,我们详细了解了Java分代收集机制的核心内容: 1. 基础概念 - 分代假设 - 内存分区 - 对象生命周期 2. 收集过程 - 新生代收集 - 对象晋升 - 老年代收集 3. 优化策略 - 参数调优 - 收集器选择 - 最佳实践 在实际应用中,我们需要: 1. 理解分代收集的原理 2. 选择合适的收集器 3. 进行适当的参数调优 4. 监控GC的运行状况 掌握这些知识对于优化Java应用的性能至关重要。