JVM初识 & JVM 内存区域划分
文章目录
- [JVM初识 & JVM 内存区域划分](#JVM初识 & JVM 内存区域划分)
- 观前须知
- [1. JVM 是什么](#1. JVM 是什么)
- [2. JVM 内存区域划分](#2. JVM 内存区域划分)
-
- [2.1 程序计数器](#2.1 程序计数器)
- [2.2 元数据区](#2.2 元数据区)
- [2.3 栈](#2.3 栈)
- [2.4 堆](#2.4 堆)
-
- 新生代区,老年代区???
- [JVM的栈和堆 VS 数据结构的栈和堆](#JVM的栈和堆 VS 数据结构的栈和堆)
- [3. 总结](#3. 总结)
观前须知
关于 JVM 的内容,是我本人,为了应付面试中,关于 JVM 的问题,编写的博客。
也就是说,这篇博客,是为了面试,而准备的。
如果你不是学习 Java编程的,或者你不打算往计算机岗位去发展的。
你是可以不看这篇博客的。
你也可以当作是科普性质,来阅读这篇博客。
1. JVM 是什么
JVM 是 Java Virtual Machine 的简称,翻译过来为 Java虚拟机 。
虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。
常见的虚拟机:JVM、VMwave、Virtual Box。
JVM 是一台被定制过的现实当中不存在的计算机。
正常来说,Java程序员,本身是不需要理解 JVM 底层内容的。
JVM 诞生的初心就是为了让你不同理解底层。
但是,有个大佬,写了一本书,叫做:深入理解 Java虚拟机

后来,很多 Java岗位的面试官,就把其中一些内容,拿出来,作为 面试题了。
我们主要讲三大块的内容:
- JVM 内存区域划分
- 类加载机制
- 垃圾回收机制(GC)
这三大块的面试题,是最多的。
第一块内容,在这篇博客中,进行介绍,后面两个,我分开两篇博客,进行介绍:
JVM 类加载机制 & 双亲委派模型
JVM 垃圾回收机制(GC)
2. JVM 内存区域划分
JVM,叫做 Java虚拟机,它是仿照真实的机器,真实的操作系统,进行设计的。
真实的操作系统中,对于进程的地址空间,是进行了 分区域 的设计 的。
JVM也就仿照操作系统的情况,也进行了分区域的设计。
JVM,是从真实的操作系统中,申请出来的内存空间 。

JVM 内存区域划分,就相当于是 JVM 自身从操作系统申请到的内存空间,再把这块内存,根据不同的功能需要,再次进行内存划分。
为什么要进行内存区域划分
举一个生活中的例子:租办公楼
有一栋写字楼(操作系统),有很多层,每一层就是一个进程。
开公司的人,就会从这层写字楼的物业这里,租一层过来,作为办公区(JVM分配到的内存空间)。
但是,这个办公区,并不是能立刻就用的,它是一个毛胚房,需要装修一下(JVM内存区域划分)。

JVM对这块内存区域,进行划分,每块区域 ,就能发挥不同的作用。
JVM 划分出了四个核心区域:
- 程序计数器
- 元数据区
- 栈
- 堆
我们可以用这么一张图,来表示这四个区域:

其中,本地方法栈 、虚拟机栈,其实都是栈,是可以合并到一起的。
接下来,我们分别介绍,这几个区域,是干嘛的。
2.1 程序计数器
程序计数器 ,这是一块很小的区域,只是用来记录,当前指令执行到哪个地址 了。

其实说白了,就是记录,当前计算机执行的这么多指令,执行到哪个 ,并且,能确保下一条指令,能够准确执行。
如果学过计算机组成原理,你应该学过,CPU中,有一块很小的区域,叫做:PC寄存器
和 程序计算器 差不多。
JVM的程序计数器 ,是 内存 上的
PC寄存器 ,是 CPU 上的
2.2 元数据区
元数据区 :保存当前类被加载好的数据

什么叫做 "类被加载好"?
写 Java代码 的时候,我们写的是 XXX.java 文件,编译之后,就会是 XXX.class 二进制的字节码文件。
想要运行这里面的代码,就需要把 XXX.class 二进制的字节码文件,加载到内存中,这就是我们后面会说的 类加载。
那么,元数据区,就是存储这些 XXX.class 二进制的字节码文件,加载好之后的内容 。
其实,加载好之后的内容,就是类被加载好的数据,就是我们说的:"类对象"。
类对象
什么叫做类对象?
当一个类被 JVM 加载时(首次使用该类时) ,JVM 会为这个类生成一个对应的 Class 类型的对象,这个对象就是类对象。例如:
java
class Person {
//属性
}
当 Person 类被加载后,JVM 会创建一个 Class< Person > 类型的对象,用于描述 Person 类的信息。
那么大家有个印象就行,不用太纠结这个东西,面试也不会问得这么深的。
元信息

我们这张图中,有两个元信息:类元信息,方法元信息
元信息,指的就是一些属性。
- 类元信息指的是:类的名字,类的访问修饰限定符(public?private?......),继承了什么类,实现了哪些接口......
- 方法元信息:方法的名字,方法的参数有几个,方法的参数都是什么数据类型的,方法的返回值是什么......
常量池

常量池 ,你可以理解为,就是一个小容器,里面存放着我们写代码时,可能会用到的常量。
- String类,有 String常量池
String常量池是Java虚拟机(JVM)中的一个特殊存储区域,用于存储字符串字面量和对这些字符串的引用。这个机制允许JVM有效地重用不可变的字符串对象,从而节省内存并提高性能。
字面量:字符串(JDK8 移动到堆中)、final常量、基本数据类型的值。
符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。
- Integer,是一个包装类,它有 Integer常量池,里面存放着 Integer 能表示的 最大值,最小值,还有其他的值,可以去调用他们。 例如:
javamain(String[] args) { int maxInt = Integer.MAX_VALUE; System.out.println("Integer的最大值是:" + maxInt); } } ```
2.3 栈
栈 :保存方法的调用关系的
写代码的时候,肯定会有方法的调用 。
每次调用方法,就会进入方法内部,执行方法内部的代码,方法执行结束之后,就会返回调用的位置,继续执行剩下的代码。
那么,方法是如何顺利返回 方法调用的位置?
于是,就引入了 栈。
在 JVM内部,就维护了一个数据结构的 栈,符合后进先出的特点。

假设,一个Java程序,main 方法中,有一个 test1方法,执行到 test1方法之后,就会进入 test1方法。
此时,栈就会 记录方法的调用关系:

那么,栈中的每一个元素,就可以认为是一个 栈帧。
栈帧,就会记录,这个方法的参数,局部变量,返回值,返回的地址(test1结束后,继续执行哪里)。
test1方法结束后,这个栈帧,就会出栈。
函数栈帧,你可以在 B站 上面搜索,进行学习。
可以用这么一张图来表示:

JVM 中的栈,空间不是很大,一般就是 几MB,几十MB 这样的情况。
这个空间,是可以通过 JVM的启动参数 来配置的。
大部分情况下,这样的内存,是够用了的,但是,少数情况下,可能会出现 "栈溢出" StackOverflow异常 。
这种情况,大概率,就是你写函数递归代码,递归出现无限递归 的情况了,这个空间被用完了,就出现 "栈溢出" StackOverflow异常。

栈 的空间,当方法结束 之后,栈帧 就会自动释放 掉,不用进行垃圾回收。
2.4 堆
堆:保存 new 的对象

new 的对象,这应该是 写Java程序,很容易见到的了。
假设,我自定义了一个类,叫做 Test类。
这段代码:
java
Test t = new Test();
这就是 new 的一个对象,我们也叫做:实例化一个对象
那么,哪个部分,是保存在 堆 上的呢?
首先 后半部分:new Test() ;
是一定保存在堆上 的。
前半部分:Test t
我们的看,t 是什么样的变量。
如果 t 是一个 局部变量 ,t 就是保存在 栈 上 的。
如果 t 是一个 成员变量 ,t 就是保存在 堆 上 的。
如果 t 是一个 静态成员变量 ,t 就是保存在 元数据区 上 的。
静态成员变量,也叫做 类变量,是所有对象共享的。
更多关于 静态成员变量 ,你可以看我的这篇博客:
JAVA 初始类和对象(下):封装 & static成员 & 内部类
这篇博客中会说到:类变量,是所有对象共享的,存储在 "方法区" 上。
其实,方法区,就是我们这篇博客说的 元数据区 。
方法区 ,是 JDK8 及之前的说法 ,现在的 JDK17 和 JDK21,都叫做元数据区。
我们可以用一张图来表示一下:

堆,是JVM中,最大的空间区域 了。
我们 new 的所有对象,都在堆上 。
我们在往集合类里面,添加元素,也是存储在堆上 的。
集合类:顺序表,链表,二叉树,哈希表......
堆上,存储了这么多的对象,元素 ,如果一些对象或元素,不再使用 的话,就需要释放掉他们,否则,就会占着空间。
如何释放?
就是我们在这篇博客中:
JVM 垃圾回收机制(GC)讲到的垃圾回收机制。
新生代区,老年代区???
这两个区,是干嘛用的,我们同样在:
JVM 垃圾回收机制(GC)中,进行详细的介绍。
JVM的栈和堆 VS 数据结构的栈和堆
此处,我们讲到的 栈和堆,和数据结构中的 栈和堆,是没有关系的。
如果,面试的时候,面试官问你 :什么是栈,什么是堆?
此时,你就要问一下面试官:是数据结构的栈和堆,还是 JVM内存划分中的 栈和堆?
这也是计算机中,很常见的一个情况:一个术语,可能有多种含义。
3. 总结
内存区域划分,是 JVM 从真实的操作系统中,申请到的内存空间,再进行划分。
这四个内存区域,是我们面试中,经常会问到的一个问题 。
这里要清楚的了解,这四个内存区域,他们的功能是什么:
- 程序计数器 :一块小的内存区域,存储下一个要执行的指令的地址(当前执行的这条指令的下一条指令是哪个)
- 元数据区 :保存类对象(类被加载好的数据),又分为元信息和常量池两块区域
- 栈 :保存方法之间的调用关系(通过栈帧来记录)
- 堆 :保存 new出来的对象
这个面试题,理解好,背起来,是比较容易的。
当然,理解透了,你自然能自圆其说,毕竟面试,是没有完美的固定答案的。
最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!