清晰区分栈内存与堆内存的用途、生命周期和管理方式
理解对象、引用、基本类型在内存中的实际存储位置
掌握字符串常量池等特殊机制对内存布局的影响
能够分析典型代码的内存表示并预测行为(如引用相等性)
了解栈溢出(StackOverflowError)与堆内存不足(OutOfMemoryError)的成因
Java 程序运行时,JVM 将内存主要划分为 栈(Stack) 和 堆(Heap) 两大区域,二者分工明确、特性迥异。
class Employee {
int id; // 实例字段 → 存储在堆中(属于对象的一部分)
String name; // 引用字段 → 存储在堆中,指向另一个堆对象
double salary; // 实例字段 → 存储在堆中
}
public class Main {
public static void display(Employee emp) { // 参数 emp → 栈(引用)
emp.display();
}
public static void main(String[] args) {
Employee emp1 = new Employee(101, "Maddy", 50000.0); // emp1 → 栈;对象 → 堆
Employee emp2 = new Employee(102, "Maddy", 60000.0); // emp2 → 栈;对象 → 堆
display(emp1); // 调用时创建新栈帧,emp 参数指向 emp1 对象
display(emp2);
}
}
关键点:
emp1和emp2是 栈中的引用变量两个
Employee对象及其内部的id、salary字段存储在 堆中字符串字面量
"Maddy"存储在 堆中的字符串常量池,两个name引用指向同一对象因此
(emp1.name == emp2.name)返回true
局部基本类型变量:如 int a = 10;
方法参数(包括对象引用)
返回地址 与 操作数栈
方法调用时压入栈帧
方法返回时自动弹出并释放
public static int add(int a, int b) {
int res = a + b; // a, b, res 均在当前方法的栈帧中
return res;
}所有变量随
add()方法结束而销毁,无需手动管理。
所有通过 new 创建的对象
数组(如 int[] arr = new int[10];)
字符串常量池(Java 7+ 位于堆中)
JVM 自动识别不可达对象并回收
不同 GC 算法(如 G1、ZGC)优化不同场景下的停顿时间与吞吐量
高速访问:连续内存 + CPU 缓存友好
自动清理:无内存泄漏风险
线程隔离:天然避免竞态条件
GC 开销:回收过程可能导致应用暂停
内存碎片:动态分配可能产生不连续空闲块
并发风险:多线程共享需显式同步(如 synchronized)
如果一个方法返回一个局部 StringBuilder 对象的引用,该对象是否会在方法结束后被回收?为什么?
在高并发系统中,大量短生命周期对象频繁分配在堆上,可能引发什么性能问题?如何缓解?
为什么字符串常量池从永久代(PermGen)移到堆内存(Java 7+)?这对字符串去重和内存管理有何影响?