Java中对象存堆、变量和调用帧存栈;堆线程共享需防并发问题,栈线程私有天然安全;堆溢出因内存泄漏,栈溢出多因深度递归;字符串字面量入常量池(堆内),new创建在堆;栈快于堆但性能瓶颈常在GC与引用关系。
Java里所有 new 出来的对象(包括数组、String 实例、自定义类实例)都落在堆中;而方法内的局部变量(如 int i = 10、String s)、方法参数、返回地址这些,全在栈里。注意:s 是引用变量,它自己在栈上,但它指向的字符串对象可能在堆(new String("abc"))或字符串常量池("abc",JDK7+ 后常量池也挪到堆里了)。
ConcurrentModificationException 或数据不一致OutOfMemoryError: Java heap space 和 StackOverflowError 看似都是“内存不够”,但成因和解法毫无交集。
Map 一直 put 不清理)、大文件流未关闭导致对象堆积、监听器/回调未反注册造成
-Xmx 和 -Xms 控堆大小,-Xss 控单个线程栈大小;增大 -Xss 可能缓解栈溢出,但会减少可创建的线程数字符串是唯一一个“写法不同、内存位置可能不同”的典型:
String a = "hello"; // 字符串字面量 → 先查字符串常量池(堆内),命中则复用,否则新建并入池
String b = new String("hello"); // 强制在堆中新建对象,即使池里已有"hello"
System.out.println(a == b); // false(引用不同)
System.out.println(a.equals(b)); // true(内容相同)
intern() 方法能把堆中字符串手动加入常量池,返回池中引用,可用于节省内存或做快速判等"abc" 就一定比 new String("abc") 更省内存——如果只是临时用一次,后者反而避免污染常量池栈确实比堆快,因为它是连续内存 + LIFO + 无GC开销;但实际性能瓶颈往往不在这里。
new ArrayList())看似“堆操作”,但现代 JVM(尤其 G1/ZGC)对新生代分配极快,真正拖慢的是后续 GC 压力或缓存行失效ThreadLocal 存大对象)会导致栈空间暴涨,容易触发 StackOverflowError
堆和栈的边界在代码里是清晰的,但它们的交互(比如栈上引用指向堆对象)才是多数内存问题真正的藏身之处——漏掉一个引用,对象就永远无法被回收。