堆和栈是分工明确的两块内存区域:栈存基本类型值、对象引用及方法调用现场,随方法进退自动释放;堆存所有new出的对象及其实例数据,由GC延迟回收,线程共享。

堆和栈不是“同一个东西的两个名字”,而是分工明确、互不替代的两块内存区域。刚学Java时,只要记住一句话:**变量本身在栈里,对象实体在堆里**,就踩准了最关键的认知起点。
存什么:轻量快照 vs 实体仓库
栈里只放三类东西:基本类型值(比如 int a = 5 中的 5)、对象的引用地址(比如 String s 中的 s 这个变量本身)、还有每次调用方法时的现场快照(参数、返回位置、局部变量表)。它不存对象,只存“指向对象的指针”。
堆里只放两类东西:所有 new 出来的对象(包括数组、String、ArrayList 等),以及这些对象内部的实际数据(比如 Person 对象里的 name 和 age 字段值)。
举个例子:
Person p = new Person("张三", 25);- p 这个变量(引用)存在栈中
- new Person(...) 创建出来的那个完整对象,连同它的字段值,全在堆中
- 栈里的 p 就像一张便签,写着“去堆内存地址 0x1A2B 找人”
怎么管:自动进出 vs 延迟回收
栈的生命周期完全由方法调用控制:方法开始 → 栈帧压入;方法结束 → 栈帧立刻弹出 → 所有内容清空。没有延迟,不靠GC,也不需要你操心。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
堆的生命周期则不确定:对象创建后一直待着,直到没有任何强引用指向它,才可能被垃圾回收器在某个后台时机清理掉。这个过程不可预测,也可能永远不发生(比如静态引用一直持有)。
- 递归太深或局部变量太多 → 栈空间耗尽 → StackOverflowError
- 不断 new 大对象,或忘了释放引用 → 堆空间撑满 → OutOfMemoryError: Java heap space
给谁用:线程私有 vs 全局共享
每个线程启动时,JVM 都会单独配一个栈——主线程一个,子线程一个,互不干扰。你在自己线程栈里定义的 int x = 10,别的线程根本看不见,天然线程安全。
堆是整个 JVM 进程共用的一块大内存,所有线程都能通过引用访问堆里的同一个对象。这就意味着:多个线程同时读写堆中一个 ArrayList,必须加锁或用 CopyOnWriteArrayList,否则数据可能错乱。
- 栈默认大小小(如 -Xss512k),改太大反而容易触发栈溢出
- 堆默认可设得很大(如 -Xmx2g),但越大 GC 压力也越明显
怎么记:一个图景 + 一个口诀
想象程序运行时的画面:main 方法一启动,栈上就立起第一张“工作台”;调用 add 方法,立刻在它上面叠一张新台子;add 返回,这张台子“啪”地撤走。而所有 new 出来的对象,全堆在房间角落的大仓库(堆)里,谁要用就拿纸条(引用)去取。
口诀:栈存“名”和“数”,堆存“物”和“体”;栈随方法进退,堆靠引用存亡。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










