文章目录
一、JVM 是什么?
JVM 就是 Java 虚拟机,虚构出来的一种计算机,是 Java 运行的基础,也是实现一次编译到处执行的关键。
这块部分可以说是 Java 中最重要的一个部分,但是工作中基本用不上,面试会考~
- 内存区域划分
- 类加载
- 垃圾回收
二、内存区域划分
就像装修房子一样,按照功能和作用,把内存区域进行划分范围,
- 堆
- 栈(虚拟机栈、本地方法栈)
- 程序计数器
- 方法区
栈和程序计数器是每个线程都各自有一份的(线程私有),堆和方法区是整个JVM进程,它有一套的(线程共享)。
不同的变量位置在哪里? \color{RoyalBlue}不同的变量位置在哪里? 不同的变量位置在哪里?
- 局部变量 栈上
- 成员变量 堆上
- 静态变量 方法区
三、类加载
类加载 本身是一个复杂的事情,学习中我们需要了解以下的就可以了:
3.1 🎨 什么是类加载?
Java 程序在运行之前,需要先编译,把 .java
变成 .class
文件(二进制字节码文件)
运行的时候, java 进程(JVM)就会读取对应的 .class
文件,并且解析内容,在内存中构造出类对象并进行初始化…
简单来说,就是把 类 从文件 加载到 内存 里
什么是类对象? \color{RoyalBlue} 什么是类对象? 什么是类对象?
类对象描述了这个类是啥样子的,有哪些属性(属性名字、类型、private/public),有哪些方法(方法名字、参数个数、参数类型、返回值类型、private/public),继承自哪个父类,实现哪些接口…
类对象也是创建实例的具体依据。
3.2 🎨 类加载具体过程
大体分为三个过程,细分五个
- 加载
- 连接
1. 验证
2. 准备
3. 解析 - 初始化
3.2.1 📚 加载
我们需要注意的是,这里的 “加载” 只是 “类加载” 中的一个阶段,
在 加载 阶段, JVM 要找到 .class
文件,读取文件内容,并且按照 .class
规范的格式来解析
3.2.2 📚 验证
检查看当前的 .class
里的内容格式是否符合要求
[.class
文件长啥样,文档上有明确描述]
3.2.3 📚 准备
给类里的静态变量分配内存空间
分配内存空间不包括进行初始化,
es:
static int a = 520;
准备阶段就是给 a 分配内存空间(四个字节),同时这些空间初始情况是全 0
3.2.4 📚 解析
初始化字符串常量,把符合引用替换成直接引用
直接引用:相当于内存地址
符合引用:相当于占位符
es:
假如代码中有一行 String s = "hi";
在类加载之前,“hi” 这个字符串常量是没有分配内存空间的,得类加载完成之后才有内存空间,s 里也就无法保留字符串常量的真实地址,只能先使用一个占位符,标记一下(这块是 “hi” 这个常量的地址),等真正给 “hi” 分配过内存之后,然后就可以用这个真正的地址替代之前的占位符
3.2.5 📚 初始化
针对类进行初始化,初始化静态成员,执行静态代码块,并且加载父类…
3.3 🎨 何时触发类加载?
使用到一个类的时候,就触发加载
类并不一定是程序一启动就加载了…是第一次使用就进行加载!!!
[有点像懒汉模式]
使用到一个类分为以下几种情况: \color{RoyalBlue} 使用到一个类分为以下几种情况: 使用到一个类分为以下几种情况:
- 创建这个类的实例
- 使用了类的静态方法 / 静态属性
- 使用了类的子类(加载子类会触发加载父类)
3.4 🎨 重点考察的部分:双亲委派模型(面试高频)
双亲委派模型 决定了按照啥样的规则来在哪些目录里去找 .class
文件
3.4.1 📚 类加载器
JVM 加载类,是由 类加载器(class loader)这样的模块来负责的
JVM 自带了多个类加载器,主要有如下三个:
- Bootstrap ClassLoader(负责加载标准库中的类)
- Extension ClassLoader(负责加载 JVM 扩展的库的类)
- Application ClassLoader(负责加载咱们自己项目里的自定义类)
上面三个类加载器各自负责一个各自的片区
描述上述类加载器相互配合的工作过程,就是 双亲委派模型
总结:
- 上述三个类加载器存在父子关系
- 进行类加载的时候,输入的内容 全限定类名,形如 java.lang.Thread
- 加载的时候,从 Application ClassLoader 开始
- 某个类加载器开始加载的时候,不会立即扫描自己负责的路径,而是先把任务委派给父 “类加载器” 让其进行处理
- 找到最上面的 Bootstrap ClassLoader ,在往上,没有父 “类加载器” 了,只能自己动手加载了
- 如果父亲找到类,就进行下面的验证、准备…如果父亲没找到类,就交给自己的儿子,继续加载
- 如果一直找到最下面的 Application ClassLoader 也没有找到类,就会抛出一个 “类没找到” 异常,类加载就失败了
按照这个顺序加载,最大的好处在于,如果程序员写了一个类,这个类正好全限定类名和标准库中的类冲突了(比如你自己写个类,叫做 java.lang.Thread),此时仍然保证类加载可以加载到标准库的类,防止代码加载错了带来问题