2023.9.1 简单认识 JVM

目录

[JVM 内存划分](#JVM 内存划分)

本地方法栈

虚拟机栈

程序计数器

堆区

元数据区

[JVM 类加载机制](#JVM 类加载机制)

加载

验证

准备

解析

初始化

[类被加载的几种情况(懒汉模式 ---> 只要被用到才会被加载)](#类被加载的几种情况(懒汉模式 ---> 只要被用到才会被加载))

双亲委派模型


JVM 内存划分

  • JVM 是一个应用程序,在它启动的时,需要从操作系统中申请内存一部分内存区域!
  • 每个 JVM 就是一个 Java 进程

划分图:


本地方法栈

  • 本地则表示 JVM 内部的 C++ 代码,就是给调用本地方法(JVM 内部的方法)所准备的栈空间

虚拟机栈

  • 给 Java 代码使用的栈空间,用来存储局部变量、方法参数、返回值 等
注意:
  • 此处的栈跟数据结构中的栈不是同一个东西
  • 数据结构的栈是一个通用的,更广泛的概念,此处的栈特指 JVM 中的一个特定空间
  • 本地方法栈 存储本地方法之间的调用关系
  • 虚拟机栈 存储方法之间的调用关系
  • 整个栈空间包含多个栈帧,每个栈帧表示一个方法,其中包含方法的入口地址、参数、返回地址、局部变量等
  • 每个线程都有自己独立的栈

程序计数器

  • 存储当前线程执行的字节码指令地址,记录当前线程执行到哪个指令,每个线程都有自己独立的程序计数器

堆区

  • 堆是 JVM 中最大的一块内存区域,用于存储对象实例和数组,一个进程只有一份,被所有线程共享,通过垃圾回收来管理内存的分配与释放

元数据区

  • 用来存储类的信息、常量、静态变量、静态成员 等

总结:

  • 局部变量在栈上
  • 普通成员变量在堆上
  • 静态成员变量在元数据区上

JVM 类加载机制

  • 指将类的字节码(.class文件)加载到内存中(元数据区),并在运行时将其转换为可执行的 Java 类

机制图:


加载

  • 通过类加载器,根据类的名字查找相对应的字节码文件,把类的字节码数据(.class文件)加载到 JVM 中并转化为其内部的数据结构

验证

  • 验证阶段 JVM 检查字节码的格式、语义、安全性等方面的问题,以确保被加载的字节码符合 JVM 规范的过程

准备

  • 准备阶段 JVM 为类的静态变量(static修饰的变量)分配内存空间,并根据其类型设置如 0 、false、null 这样的默认初始值

解析

  • 解析阶段是将常量池中的 符号引用(类、方法、字段等符号) 替换为 直接引用(指向实际内存地址的指针) 的过程,也就是初始化常量的过程

初始化

  • 初始化阶段是执行类的初始化代码的过程,JVM 会按照代码顺序执行类的静态初始化块和静态变量的赋值操作

类被加载的几种情况(懒汉模式 ---> 只要被用到才会被加载)

  • 构造类的实例
  • 调用这个类的静态方法、使用这个类的静态属性
  • 加载子类,便会加载其父类
  • 该类一旦被加载过,后续再使用便不会重复加载

双亲委派模型

  • 属于 Java 类加载机制中的一种设计模式,描述的是加载阶段,寻找 .class 文件的基本过程
  • 默认提供了三个类加载器

|----------------------------|----------------------------|------------------------------|
| Bootdtrsp Class Loader | Extension Class Loader | Application Class Loader |
| 加载标准库中的类 | 加载 JVM 拓展库中的类 | 加载用户提供的第三方库、用户项目代码 中的类 |

  • 上述三个类加载器存在父子关系 ,子 类加载器上有一个 parent 属性指向其父 类加载器

具体类加载过程:

  • 当一个类加载器收到加载类的请求时,它首先检查是否已经加载过这个类。如果已经加载过,则直接返回该类的 Class 对象

  • 如果类没有被加载过,那么该类加载器会将加载任务委派给父加载器去完成

  • 父加载器会按照相同的规则进行加载,即首先检查是否已经加载过该类,如果加载过则返回 Class对象,否则继续委派给它的父加载器

  • 这个过程一直往上委派,直到达到顶层的启动类加载器(Bootdtrsp Class Loader)。启动类加载器是 JVM 的一部分,它负责加载 Java 的标准库

  • 如果顶层的启动类加载器无法加载类,那么其子加载器会尝试自己加载类,它会通过自己的类路径搜索字节码文件,并将字节码转换为 Class 对象

例图:(加载 java.lang 类)

  • 这个加载顺序的主要目的是为了让 Bootdtrsp Class Loader 启动类能够先加载,Application Class Loader 启动类后加载。因为这样可以避免用户创建一些奇怪的类,引起不必要的 bug(用户自己写的类与标准库中的类重名)

双亲委派模型的优点:

  • 避免重复加载:每个类加载器都会缓存已加载的类,避免了同一个类被多次加载的情况

  • 安全性:通过双亲委派模型,核心类库由启动类加载器加载,这样可以保证核心类库的安全性,防止恶意代码替换核心类

  • 一致性:相同的类在不同的类加载器中只会存在一个实例,保证了类的一致性

  • 可扩展性:开发者可以通过自定义类加载器并继承现有的加载器,实现特定的类加载行为,从而实现动态的类加载和扩展

相关推荐
Anastasiozzzz18 分钟前
Java Lambda 揭秘:从匿名内部类到底层原理的深度解析
java·开发语言
骇客野人20 分钟前
通过脚本推送Docker镜像
java·docker·容器
铁蛋AI编程实战37 分钟前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
晚霞的不甘1 小时前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
马猴烧酒.1 小时前
【面试八股|JVM虚拟机】JVM虚拟机常考面试题详解
jvm·面试·职场和发展
SunnyDays10111 小时前
使用 Java 冻结 Excel 行和列:完整指南
java·冻结excel行和列
摇滚侠1 小时前
在 SpringBoot 项目中,开发工具使用 IDEA,.idea 目录下的文件需要提交吗
java·spring boot·intellij-idea
云姜.1 小时前
java多态
java·开发语言·c++
李堇1 小时前
android滚动列表VerticalRollingTextView
android·java
泉-java1 小时前
第56条:为所有导出的API元素编写文档注释 《Effective Java》
java·开发语言