线程私有区域(程序计数器、虚拟机栈、本地方法栈)不共享,由线程独占并随其生死;线程共享区域(堆、方法区/Metaspace)可被所有线程通过引用访问和并发读写,但需同步保障安全。

线程私有区域不共享,线程共享区域才真共享——边界就卡在“谁创建、谁销毁、谁可见”这三句话上。
程序计数器为什么永远不会OOM?
它只是每个线程记自己执行到哪一行字节码的小本子,大小固定(通常几个字节),由JVM硬编码分配,连GC都不管它。OutOfMemoryError根本轮不到它——内存压根没给它“溢出”的机会。
- 只记录Java方法的行号;执行
native方法时值为undefined - 线程切换靠它恢复位置,所以必须每线程一份,绝对隔离
- 别试图监控或调优它:没有JVM参数控制它,也无需关注
虚拟机栈和本地方法栈的“私有”到底多彻底?
每个线程启动时,JVM就划一块独立内存给它的VM Stack和Native Method Stack,互不触碰。哪怕两个线程调用同一个方法,它们的栈帧也是各自压栈、各自弹出。
-
StackOverflowError:递归太深、方法调用链太长,栈帧撑爆了单个栈容量 -
OutOfMemoryError:不是栈帧太多,而是你起了几百个线程,每个都配了-Xss2m,物理内存扛不住了 - HotSpot里
Native Method Stack和VM Stack实际共用同一块内存,但逻辑上仍按“服务对象”隔离(Java方法 vsnative方法)
堆和方法区怎么才算“真正共享”?
所有线程都能通过引用访问堆里的对象,也能读写方法区里的类结构、静态变量、常量池——这才是共享的实质:地址可抵达、数据可并发读写。
- 堆里对象的引用可以在线程间传递,但对象本身只存一份;
new Object()总是在堆上,除非逃逸分析+标量替换把它拆散进栈 - 方法区(JDK8+是
Metaspace)里static字段被所有线程共用,改一个,大家全看到——这也是并发问题高发区 -
Metaspace用的是本地内存,OOM时错误信息是java.lang.OutOfMemoryError: Metaspace,和堆的Java heap space完全不同
边界不是靠文档画出来的,是靠JVM实现强制隔离的:线程一死,它的栈和PC立刻清空;虚拟机一关,堆和元空间才释放。真正容易忽略的,是那些看似“共享”实则受锁保护的场景——比如多个线程往同一个ArrayList里add,堆是共享的,但没同步机制,照样出错。








