堆存对象、栈存变量和调用痕迹;堆共享导致线程不安全,栈私有天然安全;StackOverflowError源于栈溢出,OutOfMemoryError源于堆耗尽;字符串常量池在JDK7+后移入堆中。
Java中所有 new 出来的对象(包括数组、String 实例、自定义类实例)都落在堆中;而方法内的局部变量(如 int i = 10、String s)、参数、返回地址这些,全在栈里。注意:s 是引用变量,它自己在栈上,但它指向的字符串对象可能在堆(new String("abc"))或字符串常量池("abc",JDK7+ 后常量池也挪到堆里了)。
String a = "hello"; → 先查字符串常量池(堆内),命中则复用,否则新建并入池String b = new String("hello"); → 强制在堆中新建对象,即使池里已有 "hello"
a == b 返回 false(引用不同),a.equals(b) 返回 true(内容相同)堆是所有线程共用的——多个线程能同时读写同一个对象,所以要小心并发修改引发的 ConcurrentModificationException 或数据不一致;栈是线程私有的——每个线程有自己的一套栈帧,互不干扰,天然线程安全。
List list = new ArrayList();,list 变量本身在栈上,ArrayList 实例在堆上;如果把这个 list 传给另一个线程并并发修改,就可能出问题StackOverflowError 和 OutOfMemoryError: Java heap space 看似都是“内存不够”,但成因和解法毫无交集。
StackOverflowError 几乎只发生在:深度递归(比如没写好终止条件的树遍历)、超长方法链(上百层嵌套调用)、或单个方法定义了巨量局部变量(如声明几十个大数组)OutOfMemoryError 常见于:缓存没设上限(比如 Map 一直 put 不清理)、大文件流未关闭导致对象堆积、监听器/回调未反注册造成内存泄漏-Xss 控单个线程栈大小(增大可能缓解栈溢出,但会减少可创建的线程数);-Xmx 和 -Xms 控堆大小栈确实比堆快,因为它是连续内存 + LIFO + 无 GC 开销;但实际性能瓶颈往往不在访问速度,而在引用关系复杂度、GC 频率或对象生命周期管理不当。

new String())会加剧新生代 GC 压力,比“栈访问慢”影响大得多"abc" 一定比 new String("abc") 更省内存——如果只是临时用一次,后者反而避免污染常量池intern() 把堆中字符串手动加入常量池,可用于节省内存或做快速判等,但要注意常量池也是堆的一部分,滥用会导致堆内存增长过快