JVM 内存区域
JVM 在运行代码时,必须使用多块内存空间的,不同的内存空间用来存放不同的数据,再配合代码流程,才能让系统运转。
一、内存区域划分
代码运行时,需要分配内存用来存放类;运行方法、变量时,也需要存放在内存;创建对象时,也需要放在内存。

二、方法区
字节码文件(.class)加载进来的类,以及一些类似常量池的东西会放在这个区域,java 1.8 之前叫 “方法区”,之后叫 “元数据空间(Metaspace)”。

三、程序计数器
实际上 java 编译后的字节码文件才是计算机真正理解的语言。使用 idea 等工具可以查看编译后的字节码文件,大概就是这个样子。

比如 “0: aload_0” 这样的就是 “字节码指令”,计算机读到这种指令才知道执行。
所以,Java 代码通过 JVM 跑起来做的第一件事就是,把 Java 代码翻译成各种字节码指令,然后通过字节码执行引擎,字节码会被一条一条的执行。
在执行指令时,JVM 需要一个特殊的内存区域 “程序计数器”,用来记录当前执行的字节码指令的位置。
实际上,在多线程情况下,甚至是执行 main 方法的主线程都有专属的内存空间和专属的程序计数器,这样每个线程都能知道当前执行到那一条指令了。

四、Java 虚拟机栈
在执行代码指令时,会有程序计数器来记录当前指令位置。
而一些方法内的局部变量需要 JVM 分配一块区域来保存数据,这就是 Java 虚拟机栈,每个线程都有专属的虚拟机栈。
一个线程执行了一个方法,就会对这个方法调用创建一个对应的栈帧,栈帧内有这个方法的局部变量表、操作数栈、动态链接、方法出口等。
线程在执行到某个方法时就会创建一个栈帧压入自己的 Java 虚拟机栈中去,然后栈帧的局部变量表中就会存放这个方法内的局部变量。

“Java 虚拟机栈帧” 的作用就是:调用执行任何方法时,都会给方法创建栈帧,然后入栈,栈帧里存放方法执行的相关信息以及局部变量,方法执行完之后就出栈。
五、Java 堆内存
JVM 中另外一个非常关键的区域就是 Java 堆内存,这里存放的就是代码中创建的各种对象。
然后在执行方法的栈帧里的局部变量表里会有一个变量指向堆内存里的对象。一个对象对内存空间的占用,大致分两块:
- 一个是对象本身的信息
- 一个是对象实例变量占用的空间
比如对象头,如果在64位的 linux 操作系统上,会占用16字节,
如果你的实例对象内部有个 int 类型的实例变量,他会占用4个字节,
如果是 long 类型的实例变量,会占用8个字节。
如果是数组、Map之类的,那么就会占用更多的内存了。
另外JVM对这块有很多优化的地方,比如补齐机制、指针压缩机制

六、其他内存区域
在某些源码内可以看到用 native 修饰的一些方法的引用。
这些方法都是去调用本地操作系统的一些方法,用 c 语言写的或者一些类库。
这种本地方法栈跟 Java 虚拟机栈类似,存放 native 方法的局部变量表之类的信息。
还有一个区域,是不属于JVM的,通过NIO中的allocateDirect这种API,可以在Java堆外分配内存空间。
然后,通过Java虚拟机里的DirectByteBuffer来引用和操作堆外内存空间。
这种堆外分配内存可以提升性能。
七、流程总结

首先,JVM 进程启动时,会加载 -> 验证 -> 准备 -> 解析 -> 初始化,
这时 Java 代码会被编译为字节码文件(.class),
然后会使用类加载器通过双亲委派机制加载核心类,扩展类和应用程序类到 JVM 方法区。
随后 JVM 启动一个 main 线程,并分配线程的栈内存空间,
执行 main 方法时,会创建 main 方法的栈帧并压入栈。
然后字节码执行引擎执行 main 方法内的指令时,程序计数器会记录线程执行的指令位置。
其中方法内创建的对象会被存放在堆内存,被局部变量引用其地址。
方法执行完,栈帧就会从对应的虚拟机栈里出栈,main 栈帧出栈也就意味着 JVM 进程结束了。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· DeepSeek “源神”启动!「GitHub 热点速览」
· 上周热点回顾(2.17-2.23)