【JVM】内存区域划分和类加载机制

发布时间:2025-03-25 16:25:00

阅读量 : 59

一、JVM中的内存区域划分

JVM 本质上是一个 java 进程,Java 进程会从操作系统申请一大块区域给 Java 代码使用。这一大块区域会进行划分,主要划分为堆、栈、方法区1元数据区。具体分为:

  • 堆:存放对象实例(所有被 new 出来的对象)

  • 栈:存放方法调用的栈(局部变量、方法的参数、返回地址等)

  • 方法区1元数据区:存储类元数据(类结构、方法代码、静态变量、常量池等)

  • 程序计数器:记录当前线程执行的字节码指令地址(即JVM指令的行号)

  • 本地方法栈:为执行本地方法(Native Method,如JNI调用)提供栈帧

  • 虚拟机栈:执行 Java 代码


    图片

    如上图所示,对基本的内存区域划分图,下面来详细解释每个区域对应的工作。


    1.堆,主要是 new 出来的对象即成员变量

    2.栈,主要是用来维护方法之间的调用即局部变量

    3.方法区1元数据区,方法在内存中是按照一些二进制指令的形式来存储的(字节码),特殊情况热加载机制需要在字节码维度做出一些特殊的处理。因此它是类和加载之后的类对象(.class文件、Test.class文件)(静态变量等)

    4.虚拟机栈, 给 Java 代码使用的

    5.本地方法栈,给JVM 内部的本地方法使用(JVM内部通过C++方法实现)

    6.程序计数器,记录当前程序指定到哪个指令,使用简单的 long 类型存储了一个内存地址,内存地址就是下一个要执行的字节码所在的地址


    我们在编写 Java 代码时,会产生一份线程,这个线程会同时在操作系统和 Java 虚拟机存在,在 java 层面会在虚拟机栈存一份线程,而操作系统层面会在本地方法栈存一份。

    此外,堆区和元数据区,在一个JVM 进程中只有一份。而栈(本地方法栈和虚拟机栈)和程序计数器是有多份的。


二、JVM的类加载机制
2.1 类加载流程
把 .class 文件加载到内存,得到类对象这样的过程叫做类加载。程序需要想运行,就需要依赖某些指令和数据放入内存中。流程:加载、验证、准备、解析、初始化。
1.加载,找到 .class 文件,并且读文件内容。
2.验证,.class 文件是否为明确的数据格式(二进制),在官方文档中 class 文件中是以一个 C++ 结构体形式存放的,每一步都有明确的数据格式。
3.准备,给类对象分配内存空间(未初始化的空间,内存空间中的数据全是0,类对象中的静态成员也全是0),类加载最终就是为了得到类对象。
4.解析,针对字符串常量进行初始化,会将符号引用替换为直接引用。
5.初始化,针对类对象进行初始化(初始化静态成员、执行静态代码块、类如果有父类还要加载父类)以上的五个步骤都是 JVM 中的一块代码,都是人为划分出来的,
解析步骤问题,常量池内的符号引用替换为直接引用的过程?举例说明?
举例说明,在高中我暗恋一个女生,有一次全校举行看电影,我为了和我的女神坐在一起,但排列方式是按照座位来决定的。于是在去操场的过程中我一直贴着她走,最后到达地点后我成功的与她坐在了一起。而这个过程就是符号引用替换到直接引用了,也就是说在到达地点之前我是不知道能够与她坐一起而在去操场的过程中她和我的相对位置是确定的,直到最后位置确定了,她和我之间的变为绝对位置了。
在 java 层面,类名、方法名、字段名都是以字符串形式存放的,在 .class 文件中字符串常量之间会建立一个“偏移量”,即字符串常量不知道自己在内存中实际地址,在真正类加载到内中后,就会把该字符串常量放入特定的内存地址,此时字符串常量之间的相对地址还是存在的只不过类加载后给定了特殊的内存来使它们连接,也就变成了直接引用。

2.2 类加载什么时候会触发?

类加载机制并不是 JVM 一启动就把 .class 文件都加载了,而是通过一个“懒加载”的机制(懒汉模式)即非必要不加载,必要:

  • 创建了这个类的实例

  • 使用了这个类的静态方法/静态属性

  • 使用子类,会触发父类的加载


2.3 双亲委派模型

双亲委派模型为在第一个步骤加载中出现的问题,在加载中找到 .class 文件的过程。

JVM中,加载类 需要用到一组特殊的模块,类加载器。JVM中 内置了三个类加载器:

1.BootStrap ClassLoader负麦加载 Java 标准库中的类

2.Extension ClassLoader 负载加载一些非标准的但是 Sun/ Oracle 扩展库的类3.Application ClassLoader加载项目中自己写的类,以及第三方库中的类

加载一个类的过程为:

先给定一个类的全限定类名“java.long.String"的字符串,并把问题抛给 Application ClassLoaderApplication ClassLoader 作为"儿子"再把问题抛给 Extension ClassLoader ,Extension ClassLoader 作为“父亲"再把问题抛给 BootStrap ClassLoader ,BootStran ClassLoader 作为"答" 而它上方为,nul 比时就会自行处理问题,处理不了则把问题抛给"父亲"Exten.,依次类推,直到某一步能解决问题则执行后续的2345步骤否则则抛出一个异常“ClassNotFoundException"。以上的流程类似于,员工在处理问题时依次向上通报。


返回顶部