JVM初识 & JVM 内存区域划分

JVM初识 & JVM 内存区域划分

文章目录

观前须知

关于 JVM 的内容,是我本人,为了应付面试中,关于 JVM 的问题,编写的博客。

也就是说,这篇博客,是为了面试,而准备的。

如果你不是学习 Java编程的,或者你不打算往计算机岗位去发展的。

你是可以不看这篇博客的。

你也可以当作是科普性质,来阅读这篇博客。

1. JVM 是什么

JVM 是 Java Virtual Machine 的简称,翻译过来为 Java虚拟机
虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统。

常见的虚拟机:JVM、VMwave、Virtual Box。

JVM 是一台被定制过的现实当中不存在的计算机。

正常来说,Java程序员,本身是不需要理解 JVM 底层内容的。

JVM 诞生的初心就是为了让你不同理解底层。

但是,有个大佬,写了一本书,叫做:深入理解 Java虚拟机

后来,很多 Java岗位的面试官,就把其中一些内容,拿出来,作为 面试题了。

我们主要讲三大块的内容:

  1. JVM 内存区域划分
  2. 类加载机制
  3. 垃圾回收机制(GC)

这三大块的面试题,是最多的。

第一块内容,在这篇博客中,进行介绍,后面两个,我分开两篇博客,进行介绍:
JVM 类加载机制 & 双亲委派模型
JVM 垃圾回收机制(GC)

2. JVM 内存区域划分

JVM,叫做 Java虚拟机,它是仿照真实的机器,真实的操作系统,进行设计的。

真实的操作系统中,对于进程的地址空间,是进行了 分区域 的设计 的。

JVM也就仿照操作系统的情况,也进行了分区域的设计。

JVM,是从真实的操作系统中,申请出来的内存空间

JVM 内存区域划分,就相当于是 JVM 自身从操作系统申请到的内存空间,再把这块内存,根据不同的功能需要,再次进行内存划分

为什么要进行内存区域划分

举一个生活中的例子:租办公楼

有一栋写字楼(操作系统),有很多层,每一层就是一个进程。

开公司的人,就会从这层写字楼的物业这里,租一层过来,作为办公区(JVM分配到的内存空间)。

但是,这个办公区,并不是能立刻就用的,它是一个毛胚房,需要装修一下(JVM内存区域划分)。

JVM对这块内存区域,进行划分,每块区域 ,就能发挥不同的作用

JVM 划分出了四个核心区域:

  1. 程序计数器
  2. 元数据区

我们可以用这么一张图,来表示这四个区域:

其中,本地方法栈 、虚拟机栈,其实都是栈,是可以合并到一起的。

接下来,我们分别介绍,这几个区域,是干嘛的。

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 类的信息

那么大家有个印象就行,不用太纠结这个东西,面试也不会问得这么深的。

元信息

我们这张图中,有两个元信息:类元信息,方法元信息

元信息,指的就是一些属性。

  1. 元信息指的是:类的名字,类的访问修饰限定符(public?private?......),继承了什么类,实现了哪些接口......
  2. 方法元信息:方法的名字,方法的参数有几个,方法的参数都是什么数据类型的,方法的返回值是什么......

常量池


常量池 ,你可以理解为,就是一个小容器,里面存放着我们写代码时,可能会用到的常量

  1. String类,有 String常量池
    String常量池是Java虚拟机(JVM)中的一个特殊存储区域,用于存储字符串字面量和对这些字符串的引用。这个机制允许JVM有效地重用不可变的字符串对象,从而节省内存并提高性能。

字面量:字符串(JDK8 移动到堆中)、final常量、基本数据类型的值。

符号引用:类和结构的完全限定名、字段的名称和描述符、方法的名称和描述符。

  1. Integer,是一个包装类,它有 Integer常量池,里面存放着 Integer 能表示的 最大值,最小值,还有其他的值,可以去调用他们。 例如:
java 复制代码
main(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 从真实的操作系统中,申请到的内存空间,再进行划分。

四个内存区域,是我们面试中,经常会问到的一个问题

这里要清楚的了解,这四个内存区域,他们的功能是什么:

  1. 程序计数器 :一块小的内存区域,存储下一个要执行的指令的地址(当前执行的这条指令的下一条指令是哪个)
  2. 元数据区保存类对象(类被加载好的数据),又分为元信息和常量池两块区域
  3. 保存方法之间的调用关系(通过栈帧来记录)
  4. 保存 new出来的对象

这个面试题,理解好,背起来,是比较容易的。

当然,理解透了,你自然能自圆其说,毕竟面试,是没有完美的固定答案的。

最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!

相关推荐
2401_838472512 小时前
构建一个桌面版的天气预报应用
jvm·数据库·python
进阶小白猿2 小时前
Java技术八股学习Day25
java·jvm·学习
qq_192779872 小时前
将Python Web应用部署到服务器(Docker + Nginx)
jvm·数据库·python
笨手笨脚の3 小时前
深入理解 Java 虚拟机-02 对象
java·jvm·压缩指针·对象分配
蜂蜜黄油呀土豆3 小时前
Java虚拟机内存模型解析与内存管理问题
java·jvm·内存管理·内存泄漏·内存溢出
我不是8神4 小时前
RPC与 Thread 知识点全面总结
java·开发语言·jvm
2301_811232984 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
m0_561359674 小时前
高级爬虫技巧:处理JavaScript渲染(Selenium)
jvm·数据库·python
OnYoung4 小时前
实战:用OpenCV和Python进行人脸识别
jvm·数据库·python