生产Java故障绝大多数是OOM或泄漏。面试官期待你能从"症状→区域"定位——堆OOM、元空间OOM、栈溢出的排查思路完全不同。
五大区域:堆(共享,GC管)、栈(线程私有,栈帧)、方法区/元空间(类元数据,Java 8起取代PermGen)、PC寄存器、本地方法栈,再加堆外的直接内存。
堆分年轻代(Eden+两个Survivor)和老年代。大多数对象朝生夕灭——分代假说。
栈放局部变量和栈帧。StackOverflowError=递归过深。每个线程独立栈,Linux x64默认约1MB。
元空间在堆外的本地内存。"Metaspace OOM"通常是类加载器泄漏(热重载、动态代理框架)。
直接内存也在堆外。NIO ByteBuffer、Netty都在这里分配,可能堆还有富余但这里OOM。
JVM内存我按故障模式来记。堆OOM——对象太多或泄漏,上heap dump用MAT分析。元空间OOM——类加载器泄漏,多半是热重载框架或动态生成类。StackOverflowError——递归太深,一般是意外的无限递归。DirectBuffer OOM——堆外内存,堆还有但操作系统不给了。每个线程默认1MB栈,1万线程就吃掉10GB,堆都没碰——这也是响应式框架要逃离"每请求一线程"模型的原因。
MaxMetaspaceSize默认无上限——一定要设,这样类加载器泄漏能快速暴露。
线程栈大小是-Xss。要开多线程就调小,要深递归就调大。
即答侠会标注每个答案针对哪种JVM OOM——不会让你把堆OOM和元空间OOM答混。
PermGen有固定上限,应用重启/重部署容易OOM。元空间自动从本地内存扩容,JVM能动态调整,不用人工调优容量。
Java 7起挪到了堆。String.intern()放入的字符串都在堆的字符串池里,由GC回收。