《Java 100 天进阶之路》第61篇:JVM内存区域(2026版)
2026/7/23 2:44:58 网站建设 项目流程

第61篇:JVM内存区域(2026版)

📌系列导航:《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第60篇:并发编程面试压轴题 |
➡️ 下一篇:第62篇:垃圾回收器详解(待发布

🗺️ 本文阅读地图(3 分钟速览)

第51~60篇拿下了并发编程,本篇开启JVM调优与故障排查专题。JVM内存区域是理解GC、OOM、性能调优的基石,搞懂这五大区域,你就掌握了JVM的“地图”:

模块核心问题一句话回答
五大区域JVM内存分哪几块?程序计数器、虚拟机栈、本地方法栈、堆、元空间(JDK 8+)
线程私有哪些区域是线程独享的?程序计数器、虚拟机栈、本地方法栈(每个线程各有一份)
线程共享哪些区域是所有线程共用的?堆、元空间(所有线程共用,需考虑并发安全)
对象存在哪里?堆——所有new出来的对象和数组都在这里
方法调用存在哪里?虚拟机栈——每个方法调用对应一个栈帧
元空间类的信息存在哪里?元空间——存储类元数据、常量池、静态变量
OOM场景哪些区域会内存溢出?堆(OOM)、栈(StackOverflowError/OOM)、元空间(OOM)
面试最爱问高频考点有哪些?见文末 🎤 小节

一、核心知识点

JVM运行时数据区(JDK 8+)

JVM在执行Java程序时,会将其管理的内存划分为5个核心运行时数据区,根据线程私有/共享特性可分为两大类:

分类包含区域生命周期垃圾回收
线程私有程序计数器、虚拟机栈、本地方法栈与线程同生共死基本不回收
线程共享堆、元空间与JVM进程同生共死主要回收区域

💡JDK 8 核心变更:永久代(PermGen)被彻底移除,方法区的实现改为元空间(Metaspace),直接使用本地内存(Native Memory)而非JVM堆内存。

五大区域速览

二、通俗讲解(1分钟开心学)

1. 把JVM内存想象成一个图书馆

  • 程序计数器:你正在读的书签——记录你读到第几页第几行(当前执行到哪条字节码)。线程切换时,书签帮你找回刚才读到哪儿了。这本书签极小,而且永远不会弄丢(不会OOM)。

  • 虚拟机栈:你的“工作台”——每调用一个方法(读一本书),就在工作台上放一个文件夹(栈帧),方法执行完就把文件夹拿走。工作台太小时,文件夹叠太多会塌(StackOverflowError)。

  • 本地方法栈:专门处理“外文书籍”(Native方法,如C/C++写的代码)的工作台。

  • :图书馆的大仓库——所有new出来的对象和数组都存放在这里。仓库太大或太小都会出问题(OOM),所以需要管理员(GC)定期清理没人要的东西。

  • 元空间:图书馆的目录卡柜——存储类的元数据(类名、方法、字段等)。JDK 8之前这个柜子放在仓库里(永久代),容易占满;JDK 8后搬到隔壁的独立房间(本地内存),不再占用仓库空间。

2. 一句话记住五大区域

程序计数器记位置,虚拟机栈管方法,本地方法栈管Native,存对象,元空间存类信息。

三、实操代码案例 + 场景说明

3.1 栈溢出(StackOverflowError)演示

无限递归是触发栈溢出的最典型场景:

publicclassStackOverflowDemo{privateintdepth=0;publicvoidrecursion(){depth++;recursion();// 无限递归}publicstaticvoidmain(String[]args){StackOverflowDemodemo=newStackOverflowDemo();try{demo.recursion();}catch(StackOverflowErrore){System.out.println("栈溢出!递归深度:"+demo.depth);// 输出:栈溢出!递归深度:约 10000~20000(取决于栈大小)}}}

💡原因:每个方法调用都会创建一个栈帧压入虚拟机栈,无限递归导致栈帧不断堆积,超过栈的最大深度时抛出StackOverflowError。可通过-Xss参数调整栈大小(如-Xss1m)。

3.2 堆溢出(OutOfMemoryError)演示

不断创建对象并保持引用,是触发堆溢出的典型场景:

importjava.util.ArrayList;importjava.util.List;publicclassHeapOOMDemo{publicstaticvoidmain(String[]args){List<byte[]>list=newArrayList<>();try{while(true){// 每次分配 1MB,无限循环list.add(newbyte[1024*1024]);}}catch(OutOfMemoryErrore){System.out.println("堆溢出!已创建对象数:"+list.size());// 输出:堆溢出!已创建对象数:约 XXXX(取决于 -Xmx 设置)}}}

💡原因:堆内存被不断创建的对象占满,且对象一直被list引用,GC无法回收,最终抛出OutOfMemoryError: Java heap space。可通过-Xmx参数调整堆最大大小。

3.3 查看JVM内存参数
# 查看JVM默认参数java-XX:+PrintFlagsFinal-version|grep-E"HeapSize|Metaspace|StackSize"# 设置JVM参数运行程序java-Xms256m-Xmx1024m-Xss1m-XX:MetaspaceSize=128m-XX:MaxMetaspaceSize=256m HeapOOMDemo
参数含义示例
-Xms初始堆大小-Xms256m
-Xmx最大堆大小-Xmx1024m
-Xss每个线程的栈大小-Xss1m
-XX:MetaspaceSize元空间初始大小-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize元空间最大大小-XX:MaxMetaspaceSize=256m

四、避坑要点

错误/误区后果正确做法
认为程序计数器会OOM概念错误程序计数器是唯一不会OOM的区域
认为元空间在堆内调参错误JDK 8+元空间使用本地内存,不受堆大小限制
无限递归不设终止条件StackOverflowError控制递归深度或改为迭代
大量创建对象不释放引用OutOfMemoryError及时置null或使用局部变量
混淆堆和栈的存储内容理解偏差堆存对象,栈存局部变量和方法调用

五、面试高频考点

Q1:JVM内存模型包含哪几个区域?哪些是线程私有的?

五大区域:程序计数器、虚拟机栈、本地方法栈、堆、元空间。线程私有:程序计数器、虚拟机栈、本地方法栈(每个线程独有)。线程共享:堆、元空间(所有线程共用)。

Q2:堆和栈的区别?

用途:栈存方法执行信息(栈帧、局部变量),堆存对象实例和数组。②生命周期:栈随方法调用创建/销毁,堆随对象创建/GC回收。③线程归属:栈线程私有,堆线程共享。④异常:栈溢出StackOverflowError,堆溢出OutOfMemoryError

Q3:元空间和永久代的区别?

内存位置:永久代在JVM堆内存中,元空间在本地内存中。②大小限制:永久代受-XX:MaxPermSize限制,元空间默认无上限(受物理内存限制)。③OOM风险:永久代更容易OOM,元空间更灵活。

Q4:程序计数器为什么不会OOM?

程序计数器只记录当前线程执行的字节码指令地址,内存空间极小,JVM规范未对该区域规定任何内存限制,也不会抛出OutOfMemoryError

Q5:栈帧中包含哪些内容?

栈帧包含四个部分:局部变量表(存储方法参数和局部变量)、操作数栈(执行运算时的临时数据)、动态链接(将符号引用转为直接引用)、方法返回地址(方法执行完后的返回位置)。

Q6:什么情况下会抛出OutOfMemoryError: unable to create native thread

创建了过多的线程,JVM无法为新线程分配栈内存时抛出。注意:这是本地内存不足导致的,而非虚拟机栈本身的内存耗尽。

🎤 面试官追问陷阱(加分题)

追问1:“String s = new String("abc")创建了几个对象?分别存在哪里?”

👉 两个对象。"abc"字面量在字符串常量池(元空间中)创建一个对象;new String()中创建一个对象,引用指向池中的字面量。s引用变量存在虚拟机栈的局部变量表中。

追问2:“static变量存在哪个区域?”

👉 JDK 8之后,static变量(基本类型值直接存储,对象引用存储指针)存储在元空间中。static修饰的对象引用指向的对象本身仍在中。

六、练习题

  1. 分析题:写出以下代码中变量abobjstr在JVM内存中的位置(栈/堆/元空间)。

    classTest{staticinta=10;intb=20;voidmethod(){intc=30;Objectobj=newObject();Stringstr="hello";}}

    💡思路a(静态变量)在元空间;b(实例变量)在堆(随对象);c(局部变量)在栈;obj引用在栈、对象在堆;str引用在栈、字面量"hello"在字符串常量池(元空间)。

  2. 代码题:编写一个程序,分别触发StackOverflowErrorOutOfMemoryError,并观察错误信息。

  3. 场景题:某线上服务频繁报OutOfMemoryError: Metaspace,分析可能原因和解决方案。

    💡思路:元空间存储类元数据,如果应用动态加载了大量类(如热部署、动态代理、Groovy脚本等),元空间可能被占满。解决方案:增大-XX:MaxMetaspaceSize,或排查是否存在类加载器泄漏。

📊 你的学习进度

  • 当前:第61篇 / 共108篇 ·进阶篇:JVM调优与故障排查(第61~70篇)
  • ✅ 已完成:基础篇44篇 + 第45~61篇
  • 📖 正在学:第61篇
  • ⏳ 待学习:第62~108篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

👉 下一篇文章预告

🚀下一篇:《第62篇:垃圾回收器详解》

内容简介:Serial、Parallel、CMS、G1、ZGC 等垃圾回收器原理与选型,GC日志分析,JVM调参实战。

👉JVM调优专题持续深入,拿下垃圾回收器!

📌《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询