java堆不是对象分配的唯一场所,逃逸分析可使局部小对象栈上分配;堆线程共享、gc主战场,oom: java heap space表示堆满;直接内存不属堆但受maxdirectmemorysize限制;jvm栈线程独有、存栈帧,超深递归抛stackoverflowerror,线程过多抛oom: unable to create new native thread;方法区为规范概念,hotspot jdk8+用本地内存的元空间替代永久代,oom: metaspace即元空间耗尽;运行时常量池属类级别、在元空间,字符串常量池全局唯一、jdk7+起在堆中;区分oom后缀可快速定位问题区域。

Java堆(Heap)是对象分配的唯一场所吗?
不是,但绝大多数对象确实在堆里分配。JVM规范只要求对象实例在堆中分配,没禁止逃逸分析后的栈上分配——比如 EscapeAnalysis 开启时,局部小对象可能被优化到栈帧里,不进堆。
- 堆是所有线程共享的区域,GC主要作用域;分配失败会抛
OutOfMemoryError: Java heap space -
-Xms和-Xmx控制初始和最大堆大小,两者设成一样可避免扩容抖动 - 注意:直接内存(
ByteBuffer.allocateDirect())不占堆,但受-XX:MaxDirectMemorySize限制,超限抛OutOfMemoryError: Direct buffer memory
JVM栈(Java Virtual Machine Stack)存什么、怎么出错?
每个线程独有,存方法调用的栈帧(Stack Frame),每调用一次方法就压一个帧,返回就弹出。帧里含局部变量表、操作数栈、动态链接、方法出口等。
- 局部变量表存基本类型、对象引用(
reference,不是对象本身)、returnAddress;64位类型(long/double)占两个槽 - 栈深度超限会抛
StackOverflowError,常见于无限递归或过深嵌套;而栈空间不足(如线程太多)则抛OutOfMemoryError: unable to create new native thread -
-Xss设置单个线程栈大小,默认值因JVM版本和平台而异(HotSpot 64位Linux通常1MB),调太小易触发StackOverflowError,太大则线程数受限
方法区(Method Area)现在到底在哪?
方法区是JVM规范概念,不是物理内存区域。HotSpot从JDK 8起彻底移除了永久代(PermGen),用元空间(Metaspace)替代,它位于本地内存(Native Memory),不再属于JVM堆。
- 类的元信息(字段/方法/常量池)、运行时常量池、静态变量都存在元空间;字符串常量池(
StringTable)在JDK 7后已移到堆中 -
-XX:MetaspaceSize是初始触发GC的阈值,-XX:MaxMetaspaceSize才是上限;不设后者等于不限制,可能导致本地内存耗尽 - 动态生成类(如CGLIB、大量反射)容易撑爆元空间,错误信息是
OutOfMemoryError: Metaspace,不是“PermGen space”
运行时常量池(Runtime Constant Pool)和字符串常量池(StringTable)是一回事吗?
不是。运行时常量池是每个类或接口的常量池在加载后在方法区的运行时表示,包含字面量、符号引用等;字符串常量池是全局一张哈希表,专门缓存 String 对象的引用,JDK 7+起放在堆中。
-
String.intern()在JDK 6会把首次遇到的字符串拷贝进永久代的字符串常量池;JDK 7+则只记录堆中已有字符串的引用,不复制 - 大量
new String("xxx").intern()可能导致堆内存压力上升,尤其配合长生命周期的字符串使用时 - 用
jmap -histo看堆内对象分布,jstat -gc观察元空间使用趋势,比光看OOM堆栈更早发现问题
真正难的不是记住各区域名字,而是当 OutOfMemoryError 报出来时,能根据错误后缀(heap space / Metaspace / Direct buffer memory)立刻锁定区域,再结合 jstat、jmap 或 GC 日志确认是不是真满了,还是参数配歪了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











