面试常被忽略的问题——内存区域划分

你好,我是吴计可师,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。

点击下方👇关注公众号,带你一起复习后端技术,看看面试考点,补充积累技术知识,每天都为面试准备积累


聊到内存区域划分,很久多人都会说,这个简单,八股文天天背,但是当面试官问你int a=1; 这个里面有涉及到的数据,变量等放到哪个区?

额?。。


好了,我们先来复习一下功课,我们简单的就不详细过了,直接我们的经典图:


清晰明了,我们再上个表对比



划重点,图跟表中的每个文字都是重点,共享性,内存溢出的报错现象等,都会成为一个考点

背好了没有,我们继续了。


举例说明

示例代码

public class Example {    // 静态变量(存储在方法区)    private static String staticVar = "Static Variable";    // 实例变量(存储在堆中)    private int instanceVar;    // 常量(存储在运行时常量池)    private static final String CONSTANT = "Constant String";    public Example(int value) {        this.instanceVar = value; // 实例变量赋值    }    // 方法(字节码存储在方法区)    public void display() {        // 局部变量(存储在栈中)        int localVar = 42;        // 字符串常量(存储在运行时常量池)        String message = "Hello, World";        System.out.println(message + ", localVar: " + localVar + ", instanceVar: " + instanceVar);    }    public static void main(String[] args) {        // 对象(存储在堆中)        Example example = new Example(100);        example.display();    }}

数据存储分布分析

程序计数器

  • 每个线程执行程序时使用的指令地址存储在程序计数器中。

  • 示例中涉及程序计数器的内容为线程正在执行的指令地址(例如 example.display() 的调用指令)


虚拟机栈

  • 局部变量 localVar 存储在虚拟机栈中。

  • 方法调用时传递的引用变量(如 this)也存储在栈中。

  • 每个线程对应一个虚拟机栈,每个方法调用对应一个栈帧。

  • 在 display() 方法中:


本地方法栈

  • 如果程序中使用了本地方法(如 JNI),本地方法栈会存储对应的调用状态。

  • 此示例未涉及本地方法,本地方法栈为空。

  • 所有对象实例存储在堆中。

  • 示例中的对象 example 存储在堆中。

  • 对象中的实例变量(instanceVar)也存储在堆中,属于对象的一部分。

方法区

  • 存储类的元信息、静态变量、常量等。

  • 示例中的静态变量 staticVar 和静态常量 CONSTANT 存储在方法区。

  • 类 Example 的字节码也存储在方法区。


运行时常量

  • 存储编译期生成的字面量和符号引用。

  • 示例中字符串常量 "Constant String" 和 "Hello, World" 存储在运行时常量池中。


直接内存

  • 用于与操作系统进行 I/O 数据交互。

  • 示例中未直接使用直接内存。


总结表


本地方法栈

本地方法栈是 JVM 中专门为本地方法(通常由 JNI 调用的非 Java 语言方法,如 C/C++)提供的运行时内存区域。以下是一个使用本地方法栈的示例:

示例代码:java部分

public class NativeExample {    // 加载本地库    static {        System.loadLibrary("nativeLib");    }    // 声明一个本地方法(Native Method)    public native void nativeMethod();    public static void main(String[] args) {        NativeExample example = new NativeExample();        // 调用本地方法        example.nativeMethod();    }}

对应的 C 代码实现

假设我们用 C 语言实现 nativeMethod,并将其编译为一个动态链接库 nativeLib(如 nativeLib.dll 或 libnativeLib.so):

#include <jni.h>#include <stdio.h>// 本地方法实现JNIEXPORT void JNICALL Java_NativeExample_nativeMethod(JNIEnv *env, jobject obj) {    printf("Hello from native code!\n");}

本地方法栈解析

  • 当 nativeMethod 被调用时,JVM 会使用本地方法栈存储 C 方法的调用状态和相关数据。

  • 本例中,C 方法 Java_NativeExample_nativeMethod 的调用状态存储在本地方法栈中。


调用过程详解

  • JVM 加载本地库:通过 System.loadLibrary ("nativeLib") 加载本地动态链接库。

  • 调用 native 方法:执行 example.nativeMethod(),JVM 检测到这是一个 native 方法,切换到本地方法栈,并通过 JNI 调用底层的 C 方法。

  • 执行 C 方法:C 方法 Java_NativeExample_nativeMethod 被调用,实际在本地方法栈中运行,并打印字符串 "Hello from native code!"。

  • 返回 Java本地方法执行完毕后,将控制权交回 JVM,程序继续执行后续的 Java 代码。

通过该示例,清楚地看到本地方法栈在 JVM 调用非 Java 方法时的重要作用。

好了,今天就分享这么多,喜欢可以点赞,关注

END


扫码关注

一起积累后端知识
不积跬步,无以至千里
不积小流,无以成江海



喜欢此内容的人还喜欢

谈谈id那些事(五)——美团的 Leaf 的ID生成


谈谈id那些事(四)——雪花 ID(Snowflake ID)


谈谈id那些事(三)——阿里巴巴的 TDDL的ID生成


谈谈id那些事(二)——Redis 自增 ID


Java类加载过程详解