《深挖Java中的对象生命周期与垃圾回收机制》
大家好呀!👋 今天我们要聊一个Java中超级重要的话题——对象的生命周期和垃圾回收机制。
一、先来认识Java世界的"居民"——对象 👶
在Java世界里,一切皆对象。就像现实世界中的人一样,每个Java对象也有自己的"生命历程":
- 出生(创建)
- 成长(使用)
- 退休(不再被需要)
- 离世(被回收)
// 这是一个对象的"出生证明"
Person xiaoming = new Person();
这个简单的new
操作,就是Java对象的"出生仪式"啦!🎉
二、对象的完整生命周期详解 ⏳
1. 创建阶段(出生) 🍼
当写下new
关键字时,Java虚拟机(JVM)会做这些事:
- 分配内存:在堆(Heap)中划出一块地给新对象
- 初始化:调用构造方法设置初始状态
- 返回引用:把这块内存的"门牌号"给我们
// 详细创建过程
public class Person {String name;public Person(String name) {this.name = name; // 初始化名字}
}Person xiaoming = new Person("小明"); // 创建并初始化
2. 使用阶段(成长) 🏃♂️
对象创建后就开始它的"职业生涯"了:
- 被引用:通过变量名使用它
- 执行方法:调用它的各种能力
- 传递引用:可以交给其他变量
xiaoming.sayHello(); // 调用方法
Person xiaomingCopy = xiaoming; // 引用传递
3. 不可达阶段(退休) 🧓
当对象没人记得时,它就"退休"了:
- 引用消失:所有指向它的变量都指向了别处
- 等待回收:静静待在内存里,等待被清理
xiaoming = null; // 取消引用
// 现在"小明"对象就不可达了
4. 回收阶段(离世) 💀
垃圾回收器(GC)会定期:
- 标记:找出所有不可达对象
- 清理:释放它们占用的内存
// 我们无法直接调用GC,但可以建议
System.gc(); // 温馨提示:这只是一个建议,不保证立即执行哦!
三、垃圾回收机制深度剖析 🧹
1. 为什么要垃圾回收? 🤔
想象你的房间如果不打扫:
- 东西越堆越多 🗑️
- 可用空间越来越少 📦
- 最后连落脚的地方都没了 😱
Java的堆内存也是这样!所以需要定期"大扫除"~
2. 判断对象是否可回收的标准 🎯
JVM使用可达性分析算法:
- 从GC Roots出发(如静态变量、活动线程等)
- 能走通到达的对象 → 存活
- 走不通的对象 → 可回收
[外链图片转存中…(img-cRCgQsxw-1745679710773)]
3. 垃圾回收算法全家桶 �
(1) 标记-清除算法 🏷️✂️
- 标记:找出所有可回收对象
- 清除:直接释放它们的内存
- 缺点:会产生内存碎片
(2) 复制算法 📋➡️📋
- 把内存分成两半
- 只使用其中一半
- GC时把存活对象复制到另一半
- 优点:没有碎片
- 缺点:内存利用率只有50%
(3) 标记-整理算法 🏷️🧹
- 标记:找出存活对象
- 整理:把所有存活对象"挤"到内存一端
- 优点:没有碎片,内存利用率高
- 缺点:移动对象成本高
(4) 分代收集算法 �
这才是Java实际使用的算法! 根据对象年龄采用不同策略:
代 | 特点 | 算法 | 频率 |
---|---|---|---|
新生代 | 新创建的对象 | 复制算法 | 高 |
老年代 | 存活久的对象 | 标记-清除/整理 | 低 |
4. 内存模型与分代 🏗️
Java堆内存分为几个"小区":
-
新生代 (Young Generation) 👶
- Eden区(伊甸园):对象出生地
- Survivor区(幸存者区):From和To两个区
-
老年代 (Tenured Generation) 🧓
- 经历多次GC仍存活的对象
-
永久代/元空间 (PermGen/Metaspace) 🏛️
- 存放类信息等(Java 8后改为元空间)
[外链图片转存中…(img-3oC6Y5Ex-1745679710775)]
5. GC工作流程详解 🔄
- 对象诞生:先在Eden区安家
- Eden区满:触发Minor GC
- 存活对象移到Survivor区
- 年龄+1(每熬过一次GC)
- 年龄达标(默认15岁):晋升老年代
- 老年代满:触发Full GC(全局回收)
// 对象晋升示例
public class GCDemo {public static void main(String[] args) {List list = new ArrayList<>();for(int i=0; i<1000; i++) {// 不断创建大对象byte[] bigObj = new byte[1024*1024]; // 1MBlist.add(bigObj);// 每创建10个对象休息一下if(i%10 == 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}
}
四、影响GC的关键因素 ⚖️
1. 对象分配规则 📌
- 优先在Eden区分配
- 大对象直接进老年代(避免在Survivor区来回拷贝)
- 长期存活对象进老年代
- 动态年龄判断:Survivor区中同年龄对象大小超过一半时,大于等于该年龄的对象直接进老年代
2. GC性能指标 📊
- 吞吐量:GC时间占总时间的比例
- 停顿时间:GC时应用暂停的时间
- 内存占用:堆内存的大小
3. 常见的GC类型 🚦
GC类型 | 作用区域 | 特点 |
---|---|---|
Minor GC | 新生代 | 频繁但快速 |
Major GC | 老年代 | 比Minor GC慢10倍以上 |
Full GC | 整个堆 | 包括方法区,非常慢 |
五、优化GC的实用技巧 🛠️
1. 内存分配策略优化 🧠
- 避免创建过大对象:大对象直接进老年代
- 避免过多短期对象:减少Minor GC压力
- 合理设置堆大小:
-Xms512m -Xmx1024m # 初始堆512MB,最大堆1024MB
2. 引用类型选择 🔗
Java有4种引用类型,灵活使用可以优化GC:
-
强引用:普通引用,宁可OOM也不回收
Object obj = new Object(); // 强引用
-
软引用:内存不足时回收
SoftReference softRef = new SoftReference<>(new Object());
-
弱引用:下次GC必定回收
WeakReference weakRef = new WeakReference<>(new Object());
-
虚引用:跟踪对象被回收的状态
PhantomReference phantomRef = new PhantomReference<>(new Object(), queue);
3. 选择适合的GC收集器 🏎️
Java提供了多种GC实现:
收集器 | 特点 | 适用场景 |
---|---|---|
Serial | 单线程 | 客户端小应用 |
Parallel | 多线程 | 吞吐量优先 |
CMS | 并发标记清除 | 低延迟需求 |
G1 | 分区域收集 | 大堆内存 |
ZGC | 超低延迟 | 超大堆内存 |
启用G1收集器示例:
java -XX:+UseG1GC MyApp
六、实战:内存泄漏排查 🕵️♂️
1. 常见内存泄漏场景 💣
- 静态集合:静态Map不断添加元素
- 未关闭资源:数据库连接、文件流等
- 监听器未移除:注册后忘记取消
- 不合理缓存:缓存无过期策略
2. 排查工具 🧰
-
jps:查看Java进程
jps -l
-
jstat:监控GC状态
jstat -gcutil 1000 10
-
jmap:堆内存分析
jmap -heap jmap -histo:live | head -20
-
jvisualvm:图形化工具
3. 实战案例 🎬
场景:Web应用运行一段时间后OOM
排查步骤:
- 使用
jps
找到进程ID - 用
jstat
观察GC情况 - 用
jmap
导出堆内存快照 - 用MAT工具分析内存占用
- 发现是静态Map缓存未清理
修复方案:
// 原问题代码
private static Map cache = new HashMap<>();// 修复方案1:改用WeakHashMap
private static Map cache = new WeakHashMap<>();// 修复方案2:添加缓存淘汰策略
private static Map cache = new LRUMap(1000);
七、JVM参数调优指南 🎛️
1. 常用参数设置 ⚙️
# 堆内存设置
-Xms512m # 初始堆大小
-Xmx1024m # 最大堆大小
-Xmn256m # 新生代大小# 元空间设置(Metaspace)
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m# GC日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/path/to/gc.log# 使用G1收集器
-XX:+UseG1GC
2. 参数调优原则 🧭
- 避免频繁Full GC:老年代空间要足够
- 合理设置新生代:太小导致频繁Minor GC,太大会延长每次GC时间
- Survivor区比例:-XX:SurvivorRatio=8表示Eden:Survivor=8:1:1
- 监控指导调优:根据实际监控数据调整
八、Java 8-17中的GC改进 🆕
1. Java 8
- 移除了PermGen,引入Metaspace
- 默认使用Parallel Scavenge + Parallel Old组合
2. Java 9
- G1成为默认收集器
- 引入了实验性的Epsilon GC(无操作GC)
3. Java 11
- 引入ZGC(实验性)
- Epsilon GC转正
4. Java 15
- ZGC转正
- 引入Shenandoah GC
5. Java 17
- 强化ZGC和Shenandoah
- 移除CMS收集器
九、终极面试题演练 💼
Q1: 对象在内存中的生命周期是怎样的?
参考答案:
- 创建阶段:通过new关键字在堆中分配内存
- 使用阶段:被引用、调用方法、传递引用
- 不可达阶段:失去所有引用,等待回收
- 回收阶段:被垃圾收集器回收内存
Q2: 如何判断对象是否可以被回收?
参考答案:
JVM使用可达性分析算法,从GC Roots(如静态变量、活动线程栈中的引用等)出发,如果对象不可达就会被标记为可回收。即使对象有循环引用,但如果整体不可达也会被回收。
Q3: 常见的垃圾收集算法有哪些?
参考答案:
- 标记-清除:简单但会产生碎片
- 复制算法:没有碎片但内存利用率低
- 标记-整理:没有碎片且利用率高但成本高
- 分代收集:Java实际采用的策略,对不同代使用不同算法
Q4: Full GC和Minor GC有什么区别?
参考答案:
Minor GC只清理新生代,速度快且频繁;Full GC会清理整个堆(包括老年代和新生代)以及方法区,速度慢且会暂停所有应用线程。频繁Full GC通常意味着内存配置不合理或存在内存泄漏。
十、总结与展望 🌟
今天我们深入探讨了Java对象的完整生命周期和垃圾回收机制。记住几个关键点:
- 对象的一生:创建→使用→不可达→回收
- GC的核心:分代收集 + 多种算法组合
- 优化方向:减少GC频率 + 缩短GC停顿时间
- 未来趋势:ZGC/Shenandoah等低延迟GC
随着Java版本的更新,GC技术也在不断进步。理解这些原理不仅能帮我们写出更好的代码,还能在出现内存问题时快速定位和解决。
推荐阅读文章
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
什么是 Cookie?简单介绍与使用方法
-
什么是 Session?如何应用?
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
如何理解应用 Java 多线程与并发编程?
-
把握Java泛型的艺术:协变、逆变与不可变性一网打尽
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
如何理解线程安全这个概念?
-
理解 Java 桥接方法
-
Spring 整合嵌入式 Tomcat 容器
-
Tomcat 如何加载 SpringMVC 组件
-
“在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
-
“避免序列化灾难:掌握实现 Serializable 的真相!(二)”
-
如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
-
解密 Redis:如何通过 IO 多路复用征服高并发挑战!
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
-
Java 中消除 If-else 技巧总结
-
线程池的核心参数配置(仅供参考)
-
【人工智能】聊聊Transformer,深度学习的一股清流(13)
-
Java 枚举的几个常用技巧,你可以试着用用
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
-
为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)
相关文章:
《深挖Java中的对象生命周期与垃圾回收机制》
大家好呀!👋 今天我们要聊一个Java中超级重要的话题——对象的生命周期和垃圾回收机制。 一、先来认识Java世界的"居民"——对象 👶 在Java世界里,一切皆对象。就像现实世界中的人一样,每个Java对象也有自…...
C++中extern关键字详解:不同情况下的使用方式
在 C 中,extern 关键字主要用于声明变量或函数是在其他文件中定义的,从而可以在当前文件中使用这些变量或函数。下面详细介绍 extern 修饰全局变量、函数等的用法区别以及注意事项。 修饰全局变量 详细介绍 当 extern 用于修饰全局变量时,…...
【QT】深入理解 Qt 中的对象树:机制、用途与最佳实践
深入理解 Qt 中的对象树:机制、用途与最佳实践 在使用 Qt 编程时,你是否注意到很多对象可以设置“父对象”?比如: QPushButton* btn new QPushButton(parentWidget);这不是简单的层级结构,而是 Qt 强大而优雅的 对象…...
如何利用爬虫获得1688商品详情:实战指南
在电商运营和市场分析中,获取1688商品详情数据是一项重要任务。本文将详细介绍如何利用爬虫技术获取1688商品详情,包括准备工作、爬虫实现步骤以及注意事项。 一、准备工作 (一)注册1688开放平台账号 在1688开放平台注册开发者…...

网工实验——OSPF配置
网络拓扑图 配置 1.为每个路由器配置接口(略)(详细见RIP实验) 2.配置OSPF AR1 [AR1]ospf [AR1-ospf-1]area 1 [AR1-ospf-1-area-0.0.0.1]network 172.16.1.1 0.0.0.0 #精确配置网络,也可以像下面那条命令那样配置 …...

数据库系统概论-基础理论
数据库系统概述: 1、记录:计算机中表示和存储数据的一种格式或方法。 2、数据库(DataBase, DB):数据库是长期储存在计算机内、有组织、可共享的大量数据集合。可为各种用户共享。 3、数据库管理系统(Dat…...

从零开始学习人工智能(Python高级教程)Day6-Python3 正则表达式
一、Python3 正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。 在 Python 中,使用 re 模块来处理正则表达式。 re 模块提供了一组函数,允许你在字符串中进行模式匹配、搜索和替换操作。 r…...

Qt开发:项目视图(Item Views)的介绍和使用
文章目录 一、清单视图(List View)1.1 基本概念1.2 使用示例(文字列表)1.3 图标文字(图标模式)1.4 常用设置1.5 完整示例 二、树视图(Tree View)2.1 基本概念2.2 常用类简介2.3 快速…...
“ES7+ React/Redux/React-Native snippets“常用快捷前缀
请注意,这是一个常用的列表,不是扩展提供的所有前缀。最完整和最新的列表请参考扩展的官方文档或在 VS Code 中查看扩展的详情页面。 React (通常用于 .js, .jsx, .ts, .tsx): rfce: React Functional Component with Export Defaultrafce: React Arro…...

keepalived详细笔记
keepalived 是一种基于VRRP(虚拟路由器冗余协议)的高可用解决方案,主要是用于服务器的负载均衡和高可用性的保障,自动将服务切换到备份服务器上,确保业务的连续性。 工作原理: VRRP协议:一组路…...

xLua笔记
Generate Code干了什么 肉眼可见的,在Asset文件夹生成了XLua/Gen文件夹,里面有一些脚本。然后对加了[CSharpCallLua]的变量寻找引用,发现它被XLua/Gen/DelegatesGensBridge引用了。也可以在这里查哪些类型加了[CSharpCallLua]。 public over…...

一周学会Pandas2 Python数据处理与分析-Pandas2数据排序操作
锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Pandas 2提供了多种灵活的数据排序方法,主要针对 DataFrame 和 Series 对象。 1. 按值排序:s…...
obj = null; 赋值null之前没有其他引用指向obj对象,那么,当obj=null时,会被垃圾回收机制立即回收吗?
不会立即回收。 具体原因是: 赋值 obj null; 后,对象变成“不可达”,符合垃圾回收条件,但垃圾回收器并不会立刻回收它。垃圾回收是CLR自动控制的非确定性过程,什么时候执行回收取决于系统内存压力、GC策略、分代情况…...

lvm详细笔记
LVM简介 逻辑卷管理器,是Linux 系统中用于管理磁盘储存的关键技术。 LVM 则打破了磁盘分区一旦确定,其大小调整往往较为复杂,且难以灵活应对业务变化这种限制,它允许用户将多个物理分区组合卷组。例如,系统中的多个物…...

250505_HTML
HTML 1. HTML5语法与基础标签1.1 HTML5特性1.1.1 空白折叠现象1.1.2 转义字符 1.2 HTML注释1.3 基础标签1.3.1 div标签1.3.2 标题标签1.3.3 段落标签1.3.4 title1.3.5 meta 1.4 html骨架1.4.1 DTD1.4.2 html标签1.4.3 head与body标签 1.5 div标签详解1.5.1 常见class类名 2. 列…...

【HarmonyOS NEXT+AI】问答05:ArkTS和仓颉编程语言怎么选?
在“HarmonyOS NEXTAI大模型打造智能助手APP(仓颉版)”课程里面,有学员提到了这样一个问题: 鸿蒙的主推开发语言不是ArkTS吗,本课程为什么使用的是仓颉编程语言? 这里就这位同学的问题,统一做下回复,以方便…...

【专家库】Kuntal Chowdhury
昆塔尔乔杜里 Kuntal Chowdhury 是 NVIDIA 的 6G 开发者关系经理和技术布道师。他致力于推动与 NVIDIA 平台和工具的开发者和早期采用者生态系统的联系,以促进 6G 研究社区的蓬勃发展。在此之前,他是 BlueFusion, Inc. 的创始人,这是一家创新…...

【代码优化篇】强缓存和协商缓存
强缓存和协商缓存 一、强缓存与协商缓存的区别二、Vue2 前端实现强缓存(静态资源)三、Spring Boot 后端实现协商缓存(动态接口)四、测试缓存效果五、注意事项 一、强缓存与协商缓存的区别 强缓存:浏览器直接读取本地缓…...

电路中的DGND、GROUND、GROUND_REF的区别,VREF、VCC、VDD、VEE和VSS的区别?
目录 1 DGND、GROUND、GROUND_REF的区别 1.1 DGND(Digital Ground) 1.2 GROUND(Ground) 1.3 GROUND_REF(Ground Reference) 1.4 区别 2 VREF、VCC、VDD、VEE和VSS的区别 2.1 VREF(Refere…...
使用AES-CBC + HMAC-SHA256实现前后端请求安全验证
AES-CBC HMAC-SHA256 加密验证方案,下面是该方案二等 优点 与 缺点 表格,适用于文档、评审或技术选型说明。 ✅ 优点表格:AES-CBC HMAC-SHA256 加密验证方案 类别优点说明🔐 安全性使用 AES-CBC 对称加密使用 AES-128-CBC 是可…...
Excel 数据 可视化 + 自动化!Excel 对比软件
各位Excel小能手们!你们有没有过要对比两个Excel表格数据差异,却看得眼睛都花了的经历?其实啊,现在有专门的Excel文件比较软件能帮咱解决这大难题。这软件就是用来快速找出两个或多个Excel表格数据不同之处,还能把修改…...

开始使用WebStorm
目录 开始使用WebStorm打开、检出或创建项目打开项目从版本控制系统检出项目的步骤创建一个空的WebStorm项目在项目中创建新文件的步骤 熟悉WebStorm用户界面找到你要找的代码查找项目符号的调用按名称查找项目符号搜索文本片段转到符号声明历史记录 补全代码实时检查并修复代码…...

【计算机视觉】Car-Plate-Detection-OpenCV-TesseractOCR:车牌检测与识别
Car-Plate-Detection-OpenCV-TesseractOCR:车牌检测与识别技术深度解析 在计算机视觉领域,车牌检测与识别(License Plate Detection and Recognition, LPDR)是一个极具实用价值的研究方向,广泛应用于智能交通系统、安…...

【MongoDB篇】MongoDB的聚合框架!
目录 引言第一节:什么是聚合框架? 🤔第二节:管道的“发动机”们——常用聚合阶段详解!⚙️第三节:聚合表达式——管道中的“计算器”和“转换器” 🧮✏️第四节:性能优化与考量——让…...
洛谷 P1179【NOIP 2010 普及组】数字统计 —— 逐位计算
题面:P1179 [NOIP 2010 普及组] 数字统计 - 洛谷 一:题目解释: 需要求一区间内数字 2 的出现次数。注意22则记为 2 次,其它没别的... 二:思路、 思想可以考虑动态规划需要计算在每一位上数字 2 的出现次数,然后将这些…...
面试常问系列(一)-神经网络参数初始化-之自注意力机制为什么除以根号d而不是2*根号d或者3*根号d
首先先罗列几个参考文章,大家之后可以去看看,加深理解: 面试常问系列(一)-神经网络参数初始化面试常问系列(一)-神经网络参数初始化之自注意力机制_注意力机制的参数初始化怎么做-CSDN博客面试常问系列(一)-神经网络参数初始化-之-softmax-C…...

C++使用PoDoFo库处理PDF文件
📚 PoDoFo 简介 PoDoFo 是一个用 C 编写的自由开源库,专用于 读取、写入和操作 PDF 文件。它适用于需要程序化处理 PDF 文件的应用程序,比如批量生成、修改、合并、提取元数据、绘图等。 🌟 核心特点 特性说明📄 P…...

【Unity】Unity中修改网格的大小和倾斜网格
一、问题 unity中的网格(Grid)或者地面Plane组件,在使用时,都是正方形的网格,而且建立该网格后,在不改变Scale情况下,没发使其整体变大,而且也没法改变每个网格的大小,而…...
SQL 与 Python:日期维度表创建的不同选择
文章目录 一、日期维度表概述日期维度表结构 二、使用 SQL 创建日期维度表2.1 表结构设计2.2 数据插入2.3 SQL 创建方式的优势与局限 三、使用 Python 创建日期维度表3.1 依赖库引入3.2 代码实现3.3 Python 创建方式的优势与局限 四、应用场景与选择建议4.1 应用场景4.2 选择建…...

Transformer-LSTM混合模型在时序回归中的完整流程研究
Transformer-LSTM混合模型在时序回归中的完整流程研究 引言与背景 深度学习中的长期依赖建模一直是时序预测的核心问题。长短期记忆网络(LSTM)作为一种循环神经网络,因其特殊的门控结构能够有效捕捉序列的历史信息,并在时序预测…...