Java内存模型详解:栈、堆、方法区、本地方法栈与程序计数器

Java内存模型详解:栈、堆、方法区、本地方法栈与程序计数器

在 Java 面试中,JVM 内存模型几乎是必问内容。

常见问题:

  • 对象存放在哪里?
  • new出来的对象为什么在堆中?
  • 局部变量为什么在线程栈中?
  • 方法区存放什么内容?
  • 程序计数器有什么作用?
  • 栈溢出和堆溢出的区别是什么?

本文结合实际开发和面试场景,系统讲解 JVM 运行时数据区。


目录

  • JVM运行时内存结构
  • 程序计数器(Program Counter Register)
  • Java虚拟机栈(Java Virtual Machine Stack)
  • 本地方法栈(Native Method Stack)
  • 堆(Heap)
  • 方法区(Method Area)
  • JDK6、JDK7、JDK8区别
  • 对象创建过程
  • 成员变量与局部变量存储位置
  • 栈溢出与堆溢出
  • 常见面试题
  • 总结

一、JVM运行时内存结构

Java程序运行时,JVM会将内存划分为多个区域。

主要包括:

text 复制代码
┌─────────────────┐
│ 程序计数器       │
├─────────────────┤
│ Java虚拟机栈     │
├─────────────────┤
│ 本地方法栈       │
├─────────────────┤
│ 堆 Heap         │
├─────────────────┤
│ 方法区 MethodArea│
└─────────────────┘

可以简单记忆为:

text 复制代码
线程私有:

程序计数器
虚拟机栈
本地方法栈

线程共享:

堆
方法区

二、程序计数器(Program Counter Register)

程序计数器:

当前线程所执行字节码的行号指示器。


可以理解为:

text 复制代码
程序执行到哪一行了

记录器。


例如:

java 复制代码
public static void main(String[] args) {

    int a = 10;

    int b = 20;

    int c = a + b;
}

执行过程:

text 复制代码
执行第一行

↓

记录位置

↓

执行下一行

↓

记录位置

作用:

text 复制代码
保证线程切换后

能够恢复到正确执行位置

特点:

text 复制代码
线程私有

因为:

text 复制代码
每个线程执行位置都不同

面试高频:

text 复制代码
唯一不会发生OOM的内存区域

三、Java虚拟机栈(Java Virtual Machine Stack)

Java虚拟机栈简称:

text 复制代码
栈(Stack)

作用:

text 复制代码
运行Java方法

每调用一个方法:

java 复制代码
test();

都会创建:

text 复制代码
栈帧(Stack Frame)

例如:

java 复制代码
public void methodA() {

    methodB();
}

执行过程:

text 复制代码
methodA入栈

↓

methodB入栈

↓

methodB执行结束出栈

↓

methodA出栈

图示:

text 复制代码
栈顶

methodB

methodA

main

栈底

四、局部变量存放在哪里

局部变量:

java 复制代码
public void test() {

    int age = 18;

    String name = "Tom";
}

存储位置:

text 复制代码
栈帧中的局部变量表

因此:

text 复制代码
局部变量跟随方法

方法结束:

text 复制代码
立即销毁

这也是你笔记中的:

text 复制代码
局部变量在栈中

跟着方法走

的来源。


五、本地方法栈(Native Method Stack)

专门用于:

text 复制代码
Native方法

执行。


例如:

java 复制代码
public native void start0();

Thread源码:

java 复制代码
private native void start0();

native表示:

text 复制代码
不是Java实现

而是:

text 复制代码
C
C++
操作系统代码

实现。


作用:

text 复制代码
扩展Java能力

例如:

text 复制代码
文件系统

硬件交互

网络驱动

操作系统调用

这些很多都依赖本地方法。


六、堆(Heap)

堆是 JVM 最大的一块内存区域。


作用:

text 复制代码
存放对象

和:

text 复制代码
数组

例如:

java 复制代码
Student stu =
        new Student();

执行:

java 复制代码
new Student()

时:

text 复制代码
对象进入堆

引用变量:

java 复制代码
stu

存放在栈中。


图示:

text 复制代码
栈

stu
 │
 ▼

堆

Student对象

七、数组为什么在堆中

例如:

java 复制代码
int[] nums =
        new int[10];

执行:

java 复制代码
new int[10]

时:

text 复制代码
数组对象创建在堆中

引用:

java 复制代码
nums

位于栈中。


图示:

text 复制代码
栈

nums
 │
 ▼

堆

数组对象

这也是你笔记中的:

text 复制代码
数组属于引用数据类型

原因。


八、堆中的默认值

对象创建后:

java 复制代码
new User();

成员变量自动赋默认值。

例如:

java 复制代码
class User {

    int age;

    boolean flag;

    String name;
}

默认值:

text 复制代码
int       → 0

long      → 0L

float     → 0.0

double    → 0.0

char      → '\u0000'

boolean   → false

引用类型  → null

原因:

text 复制代码
堆内存初始化

时自动赋值。


九、方法区(Method Area)

方法区:

存储类信息、方法信息、常量、静态变量等数据。


例如:

java 复制代码
public class User {

    private String name;

    public void save() {

    }
}

类加载后:

text 复制代码
User类信息

save方法信息

字段信息

进入方法区。


因此:

text 复制代码
代码运行前

先加载到方法区

这与你笔记中的:

text 复制代码
代码预备区

概念类似。


十、静态变量存放在哪里

例如:

java 复制代码
public class User {

    static int count = 0;
}

JDK6:

text 复制代码
方法区(永久代)

JDK7以后:

text 复制代码
堆中

JDK8:

text 复制代码
元空间(Metaspace)

类信息在元空间

静态变量在堆中

因此你的笔记:

text 复制代码
静态变量在堆中

对于现代JDK是正确的。


十一、对象创建过程

代码:

java 复制代码
User user =
        new User();

执行过程:

第一步

检查类是否加载。

text 复制代码
User.class

第二步

在堆中分配内存。

text 复制代码
创建对象

第三步

成员变量赋默认值。

例如:

text 复制代码
0

false

null

第四步

执行构造方法。

java 复制代码
public User() {

}

第五步

返回对象地址。


最终:

text 复制代码
user

↓

对象地址

十二、成员变量与局部变量区别

成员变量

定义:

java 复制代码
class User {

    int age;
}

特点:

text 复制代码
定义在类中

属于对象

存放于堆

生命周期:

text 复制代码
对象创建开始

对象销毁结束

局部变量

定义:

java 复制代码
public void test() {

    int age = 18;
}

特点:

text 复制代码
定义在方法中

存放于栈

生命周期:

text 复制代码
方法调用开始

方法结束销毁

十三、栈溢出(StackOverflowError)

示例:

java 复制代码
public void test() {

    test();
}

执行:

text 复制代码
不断调用自己

不断入栈

最终:

text 复制代码
栈空间耗尽

异常:

text 复制代码
StackOverflowError

最典型场景:

text 复制代码
递归没有结束条件

十四、堆溢出(OutOfMemoryError)

示例:

java 复制代码
List<Object> list =
        new ArrayList<>();

while (true) {

    list.add(new Object());
}

结果:

text 复制代码
对象无限增长

最终:

text 复制代码
Java heap space

异常:

text 复制代码
OutOfMemoryError

十五、线程私有与线程共享

区域 是否共享
程序计数器
虚拟机栈
本地方法栈
方法区

记忆口诀:

text 复制代码
三私有

两共享

十六、面试高频问题

面试题1

对象存放在哪里?

答案:

text 复制代码
堆中

面试题2

局部变量存放在哪里?

答案:

text 复制代码
虚拟机栈

面试题3

成员变量存放在哪里?

答案:

text 复制代码
对象中

对象位于堆

面试题4

方法信息存放在哪里?

答案:

text 复制代码
方法区

面试题5

StackOverflowError原因?

答案:

text 复制代码
栈空间耗尽

面试题6

OutOfMemoryError原因?

答案:

text 复制代码
堆空间耗尽

面试题7

哪个区域不会发生OOM?

答案:

text 复制代码
程序计数器

十七、JVM内存结构速记图

text 复制代码
线程私有

程序计数器
虚拟机栈
本地方法栈

────────────

线程共享

堆
方法区

对象:

text 复制代码

数组:

text 复制代码

局部变量:

text 复制代码

类信息:

text 复制代码
方法区

总结

JVM运行时内存主要包括:

text 复制代码
程序计数器

虚拟机栈

本地方法栈

堆

方法区

其中:

text 复制代码
对象、数组 → 堆

局部变量 → 栈

类信息、方法信息 → 方法区

需要牢记:

text 复制代码
栈跟着方法走

堆跟着对象走

以及:

text 复制代码
程序计数器是唯一不会发生OOM的区域

这是 JVM 面试中的经典考点。


相关推荐
ywl4708120871 小时前
泛型extends和super的区别
java
惜缘破军1 小时前
基于 Spring Boot 4 和 Spring Cloud 2025 的微服务基础框架 hdfk7-boot
java
换个昵称都难1 小时前
WebRTC QoS 实战:从原理到弱网优化
开发语言·php·webrtc
小白起 v1 小时前
从零搭建一个现代化的验证码登录系统:Spring Boot + 阿里云短信实战教程
java·阿里云
爱吃生蚝的于勒2 小时前
QT开发第三章——常用控件
linux·服务器·开发语言·前端·javascript·c++·qt
未若君雅裁2 小时前
工厂模式详解:简单工厂、工厂方法与抽象工厂
java·开发语言
不会写DN2 小时前
通过php 中的Route:: 的写法了解什么是静态类调用
android·java·php
小刘|2 小时前
SpringAIAlibaba快速接入阿里云百炼
java·spring boot·spring·maven
我命由我123452 小时前
由 ImageView 获取到的 Drawable 对象,它的 intrinsicWidth、intrinsicWidth 与实际图片的尺寸
java·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime