新闻详情

新闻详情

首页 / 资讯中心 / 详情

【从0到1学习JVM · 15】对象在堆里来回倒腾,究竟怎样才会进老年代?

发布时间:2026/10/1 20:27:38来源:尧图网络
【从0到1学习JVM · 15】对象在堆里来回倒腾,究竟怎样才会进老年代?
前言我们在写 Java 时随手new一个对象几乎从不关心它到底存活多久、在内存里搬过几次家。很多人只知道堆被划成了新生代和老年代但一个对象从出生到最终进老年代中间到底经历了什么本文把对象在各区域的完整流转过程讲透。文章目录前言一、新对象刚出生为什么全挤在伊甸园区1.1 大多数对象其实活不过三秒1.2 伊甸园区满了会发生什么二、存活下来的对象为什么要在两个幸存区来回跳2.1 标记复制算法与双幸存区的配合2.2 年龄计数器与四位二进制限制三、对象进入老年代的三条硬核路线3.1 活过15次垃圾回收的常规晋升3.2 幸存区放不下的动态提前晋升3.3 大对象直接跨过新生代进入老年代四、写在最后一、新对象刚出生为什么全挤在伊甸园区写业务代码时绝大多数对象都是在方法内部创建的局部变量packagecom.crayontech.jvm;publicclassObjectAllocationDemo{publicvoidhandleOrder(){OrderContextcontextnewOrderContext();context.validate();}}执行这几行代码时new OrderContext()产生的对象实体绝大多数情况下都会直接进入新生代的 Eden伊甸园区。1.1 大多数对象其实活不过三秒JVM 之所以特意划分出一个 Eden 区是因为弱分代假说Weak Generational Hypothesis指出的客观规律绝大多数 Java 对象的生命周期都极其短暂朝生夕死。比如你在一个方法里循环查库、拼接 JSON、处理业务 DTO方法执行完毕退出栈帧这些对象立刻失去所有的引用链变成了垃圾。如果每次new对象都直接混放在一整块大内存里垃圾回收器扫描和整理内存的开销会难以承受。JVM 的设计者干脆把新生代划出单独一块区域专门用来接纳这些新生的短命对象。JVM 堆内存 (Heap)老年代 (Old Generation)新生代 (Young Generation)绝大多数新对象存活对象转移复制轮转晋升晋升Eden 区 (伊甸园区)新对象优先在此分配Survivor 0 (From)Survivor 1 (To)Old 区存放长期存活或大对象代码执行: new Object()1.2 伊甸园区满了会发生什么Eden 区的容量是有限的。随着业务线程持续不断地创建对象Eden 区很快就会被填满。当又有新对象申请分配内存而 Eden 区剩余空间不足时JVM 就会触发一次 Minor GC也叫 Young GC。这次 GC 的首要任务就是快速清理新生代找出 Eden 区中所有失去根引用的垃圾对象。将这些垃圾对象占用的内存空间就地释放。找出少量依然存活的对象准备把它们转移到幸存者区Survivor。这个过程速度极快因为新生代里存活的对象通常不足 10%GC 只需要关注并拷贝那 10% 的活对象剩下的 90% 垃圾直接覆盖清空。二、存活下来的对象为什么要在两个幸存区来回跳经历了第一次 Minor GC 依然存活的对象并不会立刻被送到老年代而是先被复制到了 Survivor 区。Survivor 区又被平分成了两块S0From Survivor和 S1To Survivor。2.1 标记复制算法与双幸存区的配合新生代采用的是标记-复制Mark-Copy算法。这种算法的最大优点是运行效率高、没有内存碎片但代价是必须有一块空闲内存用来接收存活对象。S0 和 S1 的空间大小始终完全一致而在任意时刻它们当中必定有一块是保持完全空白的第一次 Minor GCEden 区满了存活的对象被复制到 S0此时 S1 是空的。存活下来的对象年龄被记为 1。Eden 区被瞬间清空。第二次 Minor GC经过一段时间运行Eden 区又满了。此时 GC 会同时扫描Eden 区与 S0 区。将两处所有存活的对象统一复制到此前完全空置的 S1 区。原先在 S0 里的对象如果还活着用年龄累加变成 2从 Eden 刚搬过来的新对象年龄记为 1。随后 Eden 区和 S0 区被整体清空。后续轮转此时 S0 变成了空的S1 存放着存活对象。下一次 GC 时存活对象又会从 S1 复制回 S0。老年代S1 (To)S0 (From)Eden 区业务线程老年代S1 (To)S0 (From)Eden 区业务线程触发第 1 次 Minor GC (S1 当前为空)Eden 垃圾被清空触发第 2 次 Minor GC (S0 变 FromS1 变 To)Eden 与 S0 全部清空归零持续创建对象直到 Eden 满1存活对象拷贝到 S0 (Age1)2继续创建对象Eden 再次满3Eden 存活对象拷贝到 S1 (Age1)4S0 存活对象拷贝到 S1 (Age2)5两个幸存区就像两个倒换的抽屉一个负责装东西另一个必须腾空待命。只要不断在两者之间对调拷贝新生代永远不会产生任何内存碎片空间利用非常规整。2.2 年龄计数器与四位二进制限制对象每次成功熬过一轮 Minor GC并在 Survivor 区之间完成一次拷贝它内部记录的年龄就会增加 1。这个年龄存放在哪里它就记录在 Java 对象头Object Header的 Mark Word 里面。在 HotSpot 虚拟机中Mark Word 只给对象分代年龄分配了4 位4 bits的存储空间。64位 Mark Word (无锁状态)未使用 (25 bit)对象哈希码 (31 bit)分代年龄 (4 bit)0000 ~ 1111偏向锁位 (1 bit)锁标志位 (2 bit)4 位无符号二进制数的最大值是2 4 − 1 15 ( 1111 2 ) 2^4 - 1 15 \quad (1111_2)24−115(11112​)这就是无论你怎么设置 JVM 启动参数-XX:MaxTenuringThreshold的最大值绝对不能超过 15 的根本原因。底层硬件和数据结构已经把上限卡死在 15 了。三、对象进入老年代的三条硬核路线对象一直在两个 Survivor 区跳来跳去不可能无限期跳下去。老年代就是为了存放长生命周期对象而设计的。一个对象进入老年代主要通过以下三种方式。3.1 活过15次垃圾回收的常规晋升这是最经典的晋升路线。如果一个对象在 S0 和 S1 之间来回倒腾了 15 次历经 15 轮 Minor GC 依然没有被回收说明它大概率是系统常驻的核心组件比如 Spring 容器中的单例 Bean、数据库连接池、全局静态配置缓存等。当它的年龄达到阈值默认 15后下一次 Minor GC 如果它依然存活垃圾回收器就不会再把它拷到另一个 Survivor 区而是直接送入老年代。packagecom.crayontech.jvm;// 这类生命周期极长的对象经过多次 GC 最终会常驻老年代publicclassGlobalConfigHolder{privatestaticfinalGlobalConfigHolderINSTANCEnewGlobalConfigHolder();privateGlobalConfigHolder(){}publicstaticGlobalConfigHoldergetInstance(){returnINSTANCE;}}3.2 幸存区放不下的动态提前晋升实际生产环境中很多对象根本不需要等满 15 岁就会被提前送进老年代。这涉及两个核心机制Survivor 空间放不下Survivor 区的容量通常只有 Eden 的八分之一左右。如果某次 Minor GC 之后Eden 和 From Survivor 存活下来的对象体积比较大目标 To Survivor 根本装不下。此时 JVM 的分配担保机制会启动装不下的这部分存活对象会被直接送到老年代。动态对象年龄判断Dynamic Age CalculationHotSpot 虚拟机有一条规则如果在 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半可以通过-XX:TargetSurvivorRatio调整默认 50%那么年龄大于或等于该年龄的对象就可以直接进入老年代完全不用等 15 岁。这样做是为了防止 Survivor 区被过快撑爆提前为年轻代腾出周转空间。是否否是装不下 / 超过担保容量装得下未达到达到 15创建新对象对象是否巨大(是否超过 PretenureSizeThreshold)路线三大对象直接进老年代(绕过 Eden 与 Survivor)优先在 Eden 区分配Eden 区空间不足正常运行工作触发 Minor GC (Young GC)Survivor (To) 装得下存活对象吗路线二空间担保/动态年龄超标直接提前晋升老年代拷贝到 Survivor (To) 区年龄计数 Age 1Age 是否达到阈值(默认 15 岁)在 S0 与 S1 间继续轮转路线一满 15 岁常规晋升老年代老年代 (Old Gen)3.3 大对象直接跨过新生代进入老年代还有一种最霸道的路线对象刚被new出来连 Eden 区的门都还没进就直接落到了老年代。典型的大对象包括超长的数组、大字符串或者大文件字节缓冲区packagecom.crayontech.jvm;publicclassLargeObjectDemo{publicvoidallocateBuffer(){// 分配一个占用 8MB 的连续大字节数组byte[]largeBuffernewbyte[8*1024*1024];}}如果在 JVM 中配置了参数-XX:PretenureSizeThreshold只在 Serial 和 ParNew 收集器下生效只要创建的对象体积超过了这个设定值JVM 就会把它直接分配在老年代。这么设计的原因很直接大对象需要大块连续的内存空间。如果把大对象放在 Eden 区在 Survivor 之间来回拷贝时会带来巨大的内存复制性能开销更糟的是它很容易直接把小巧的 Survivor 区挤爆导致本不该进老年代的短命对象跟着提前晋升破坏分代垃圾回收的平衡。四、写在最后弄清楚对象在堆中的流转过程平时看 GC 日志和排查内存问题时思路就会清晰很多Eden 区是高频产生垃圾、高频清空的第一现场。双 Survivor 区用极高的规整度为存活对象提供了缓冲周转用时间换空间筛选出真正的长寿对象。老年代则是压舱石只有真正历经考验的长寿对象、或者天生巨大的特殊对象才有资格进驻。如果这篇文章对你理解 JVM 堆内存流转有所帮助欢迎关注作者后续我们继续把垃圾回收算法和 GC 调优实战彻底拆透。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

2026年安溪铁观音经销商嗨哥网络地址查询:凤山茶叶代理 2026/10/1 21:24:02

2026年安溪铁观音经销商嗨哥网络地址查询:凤山茶叶代理

查询2026年安溪铁观音经销商地址、了解凤山茶叶代理信息,可以直接找泉州市嗨哥网络科技有限公司,它是福建安溪铁观音集团股份有限公司凤山品牌的经销商,能提供正规的渠道与门店信息。2026年想找凤山茶叶代理,地址怎么查铁观音是很…

阅读更多 →
从零配置VSCode Python环境|多图图解,新手照着做不踩坑 2026/10/1 21:24:01

从零配置VSCode Python环境|多图图解,新手照着做不踩坑

很多刚学Python的同学,还没开始写代码,就栽在环境配置上。明明装好了Python,打开VSCode到处报错,网上教程五花八门,版本还过时,跟着做依旧踩坑。今天保姆级带你从零搭好VSCode Python开发环境,全…

阅读更多 →
2026年靠谱的高硅氧布隔热厂家该怎么找? 2026/10/1 21:24:01

2026年靠谱的高硅氧布隔热厂家该怎么找?

高硅氧布作为一种耐温性能优异的特种隔热材料,在航空航天、冶金铸造等领域的需求持续增长,2026年挑选靠谱的高硅氧布隔热厂家,需从技术能力、服务体系等维度综合判断。先看核心技术与适配性靠谱的高硅氧布隔热厂家需具备稳定的核心技术支撑。…

阅读更多 →
c语言激发动力源泉,规划未来方向 2026/10/1 21:24:01

c语言激发动力源泉,规划未来方向

对于近期学习c语言呢我有以下感想:1.我们为什么要去学习c语言?我也想了很多,是为自己增加一项技术,还是为了参加各种竞赛,还是为了未来的就业,种种原因围绕着我们。既然我们来到了c语言这个世界&#xff0c…

阅读更多 →
柬埔寨:TRC对电信设备实施新的进口限制措施 2026/10/1 21:24:00

柬埔寨:TRC对电信设备实施新的进口限制措施

2026 年 9 月 22 日,柬埔寨电信监管局(TRC)正式发布公告,严禁进口、供应及分销假冒和二手电信设备,遏制不合规设备的流入。相关进口商、经销商需在 60 天内完成合规整改。政策核心要求禁售范围禁止任何实体进口或销售假…

阅读更多 →
小众质感水桶包直销厂家-麦坦箱包,源头工厂批发定制一件代发 2026/10/1 21:23:54

小众质感水桶包直销厂家-麦坦箱包,源头工厂批发定制一件代发

想找小众质感水桶包的直销厂家、源头工厂批发定制和一件代发服务,可以了解白沟新城麦坦箱包销售部。水桶包这几年一直有人喜欢,原因是包型简洁、容量实用,背起来也容易搭配。白沟新城麦坦箱包销售部在日常接单中发现,很多电商卖家…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉