一、概述
1. 概念
java虚拟机,在实际的计算机上仿真模拟各种计算机功能。
2. 思维导图
3.jvm位置图
4. jvm体系结构
4.1 简图
高效记忆:JVM架构简图
(1)头发:头上的假发(.java File)被发卡(.class File)卡得非常热(class loader)
(2)眼睛:眼睛看到很多深圳本地(**本地方法栈)**人站(栈)在大街上,人多的计数器(计数器)都算不过来,原来周杰伦来开演唱会了!
(3)耳朵:耳朵没有办法(方法区)听到外面的声音,因为房子是全部用隔音墙堆(堆)的。
(4)鼻子:我的鼻子直接(直接内存)闻到了很远(元数据区)的红烧牛肉的味道,太香了!
(5)嘴巴:我用本地方法(本地方法库)吃东莞烧鹅,然后直接口(本地方法接口)吃了,最后无法执行(执行引擎)今天的演讲任务了。
4.2 详细图
5.ClassLoader(类加载器)
5.1 类加载
5.1.1 概念
(1)Java File 文件经过编译后变成 .class File字节码文件
(2)class File字节码文件通过类加载器加载到 JVM 虚拟机中。
5.1.2 运行时数据区
虚拟机主要的 5 大块:
(1)栈和本地方法栈和计数器都是独享区域,不存在线程安全问题。
(2)方法区,堆都为线程共享区域,有线程安全问题。
(3)JVM 的调优主要围绕栈、堆两大块进行。
5.2 类加载
5.2.1流程图
高效记忆 :类加载流程
(1)今天我太无聊了,于是下载(加载)了抖音安装包,安装抖音的时候,手机会先验证(验证)安装包的安全性。
(2)安装好后抖音会做好自启动、开启定位等准备(准备),然后会解析(解析)我的用户名和密码等信息,最后初始化(初始化)信息。
(3)当我使用(使用)一天后觉得浪费了太多时间,于是又把抖音卸载(卸载)了。
上述可以结合自己对具体过程联合记忆,如:
加载:加了2个擂(2进制类)台,擂台上东西堆成了卡车模特队形象。(class对象)
验证:验证文件格式中是否有字节码、引用符号
准备:准备嫁给变态(静态变量)却被默认(初始默认值)了
解析:引用符号直接引用
初始化:变态被赋予正确的初始价值
5.2.2 顺序
(1)加载、验证、准备和初始化这4个阶段发生的顺序是确定的,而解析阶段则不一定。
(2)解析可以在初始化之后开始,是为了支持 Java 语言的运行时绑定
(3)另外这5个阶段是按顺序开始,而不是按顺序进行或完成。
(4)这些阶段通常都是互相交叉地混合进行,通常在一个阶段执行的过程中调用或激活另一个阶段。
5.2.3 概述
(1)加载:查找并加载类的二进制数据,在 Java 堆中创建一个 java.lang.Class 类的对象;
(2)连接:连接包含:验证、准备、初始化;
- 验证:文件格式、元数据、字节码、符号引用验证;
- 准备:为类的静态变量分配内存,并将其初始化为默认值;
- 解析:把类中的符号引用转换为直接引用。
(3)初始化:为类的静态变量赋予正确的初始值;
- BootStrap ClassLoader:rt.jar
- Extention ClassLoader:加载扩展的 jar 包
- App ClassLoader:指定的 classpath 下面的 jar包
- Custom ClassLoader:自定义的类加载器
(4)使用:new出对象程序中使用;
(5)卸载:执行垃圾回收。
5.3 类加载器
高效记忆:类加载分类
我引导(引导类加载器)爸妈扩展了 (扩展类加载器)微信聊天应用(应用类加载器)的用户自定义表情(用户自定义类加载器)。
5.3.1 启动/引导类加载器
Bootstrap
(1)主要加载的是JVM自身需要的类,使用C++语言实现,
(2)负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中
,只加载包名为java、javax、sun等开头的类。
5.3.2 扩展类加载器
Extension
(1)加载系统类路径java -classpath或-D java.class.path 指定路径下的类库,开发者可以直接使用
(2)一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
5.3.3 系统/应用类加载器
System、Application
5.3.4 用户自定义加载器
**Custom **
5.4 类加载实例
5.4.1 图
5.4.2 获取类加载器的代码
class Person{
}
public class TestClassLoader {
public static void main(String[] args) {
Person person_01 = new Person();
Person person_02 = new Person();
Person person_03 = new Person();
//发现person_01,person_02,person_03的hashCode一致,代表这三个实例化对象隶属于一个Class,即Person
System.out.println(person_01.hashCode());
System.out.println(person_02.hashCode());
System.out.println(person_03.hashCode());
//Person实例化对象person_01通过getClass()方法得到Class对象Person
Class Person = person_01.getClass();
//Person通过getClassLoader()方法得到系统类加载器
ClassLoader myClassLoader = Person.getClassLoader();
System.out.println(myClassLoader.hashCode());
//加载器对象myClassLoader通过getParent()方法得到拓展类加载器
ClassLoader myParentClassLoader = myClassLoader.getParent();
System.out.println(myParentClassLoader.hashCode());
//加载器对象myGPClassLoader通过getParent()方法得到引导类加载器
ClassLoader myGPClassLoader = myParentClassLoader.getParent();
System.out.println(myGPClassLoader.hashCode()); //发现报错,无法通过方法获取引导类加载器
}
}
6.双亲委派机制
6.1 类加载器间关系图
备注:
父类加载器并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码。
6.2 工作原理
高效记忆:双亲委派原理
(1)子类(类加载器)班级学生会收到校外联谊请求(类加载请求)访问后,不是自己班级学生会先去决定并执行(加载执行)接待,而是先委托给父类(父类加载器)系级学生会去决定接待,
(2)如果父类(父类加载器)系级学生会还有父类(父类加载器)院级学生会,则进一步向上委托,直到顶级父类(顶级类加载器)总学生会。
(3)父类(父类加载器)总学生会决定接待后,如果需要父类(父类加载器)总学生会执行接待,就由父类(父类加载器)总学生会直接执行接待
(4)如果父类(父类加载器)总学生会不需要亲自执行接待,则让他子类(子类加载器)院级学生会去执行接待。
(1)一个类加载器收到类加载请求后并不会自己先去加载,而是把请求委托给父类的加载器去执行
(2)如果父类加载器还存在其父类加载器,则进一步向上委托直到顶级类加载器,
(3)如果父类加载器完成类加载,就成功返回
(4)如果父类加载器未完成类加载,其子类加载器就会去加载。
6.3 优点
(1)确保类只被加载一次, 避免重复加载
(2)用户即使自定义同样路径的java.lang.Integer类,最后加载还是jdk的Integer类。
避免了恶意篡改核心包的风险。
7.沙箱安全机制
7.1 概念
(1)将 Java 代码限定在虚拟机特定的运行范围中,并且严格限制代码对本地系统资源访问。
(2)保证对代码的有效隔离,防止对本地系统造成破坏。
(3)沙箱主要限制CPU、内存、文件系统、网络等系统资源访问。
7.2 沙箱的基本组件
7.2.1 字节码校验器(bytecode verifier)
(1)确保Java类文件遵循Java语言规范,帮助Java程序实现内存保护
(2)核心类等不用经过字节码校验
7.2.2 类加载(class loader)
(1)防止恶意代码去干涉善意的代码;
(2)守护了被信任的类库边界;
(3)将代码归入保护域,确定了代码可以进行哪些操作。
二、jvm详解
运行时数据区域
(1)Java 程序在运行时,会为 JVM 单独划出一块内存区域。
(2)这块内存区域又可以再次划分出一块运行时数据区,含以下五个部分:
高效记忆:JVM运行区数据类型
(1)我在客栈(栈)里的8(8大基本数据类型)个对象饮用(对象引用)青梅煮酒,
(2)因为不讲究喝酒方法(方法区)醉的一塌糊涂,最后都变态(静态变量)地躺在敞亮水池(常量、运行时常量池)中,累的没有办法起来(类、方法),
(3)等我醒来发现,前面都是我堆(堆)集的梦里new出来的对象(new的对象实例)
1.本地方法栈(Native)
Native Method Stack
1.1 本地方法
(1)凡是带了native关键字的,说明java的作用范围达不到了,回去调用底层c语言的库!
(2)会进入本地方法栈,然后去调用本地方法接口将native方法引入执行
1.2 本地方法栈
(1)线程独享区,负责登记native方法
(2)在执行引擎( Execution Engine )执行的时候,通过本地方法接口(JNI)加载本地方法库中的方法。
(3)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;
(4)如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError异常。
2.虚拟机栈:简称栈
2.1 概述
(1)线程私有,每个线程独享一个虚拟机栈,生命周期与线程相同。
(2)描述Java 方法执行的内存模型:
- 每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)
- 栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。
- 每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
(3)栈内存主管程序的运行、生命周期和线程同步。线程结束,栈内存就释放,不存在垃圾回收问题。
(4)栈内存中运行:8大基本类型+对象引用.
(5)与本地方法栈一样,虚拟机栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。
2.2 栈图
2.3 栈流程
2.3.1 流程概述
例:int a=7;
(1)局部变量表:存放局部变量(a)
(2)操作数栈:存放操作数(7)
(3)动态链接:将符号引用转成直接引用(符号引用就是你知道调用了谁,直接引用就是你拿到将要要调用的方法的地址)
(4)方法出口:方法结束
2.3.2 流程图
3.PC程序计数器
(1)线程私有,每个线程都有一个。
(2)记录当前线程所执行的位置。
(3)当线程获得 CPU 的执行权的时候,就直接从记录的位置开始执行。
分支、循环、跳转、异常处理也都依赖这个程序计数器来完成。
4.方法区(Method Area)
(1)所有线程共享区域,线程不安全区域。
(2)存储已被虚拟机加载的类、方法信息等,如类信息(构造方法、接口定义)(Class)、静态变量(static)、常量(final)、运行时常量池、即时编译器编译后的代码等数据。但是实例变量存在堆内存中
(3)无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。
5.堆
5.1 概述
(1)线程共享区域,因此是线程不安全的。
(2)一个JVM只有一个堆内存,堆内存的大小是可以调节的
(3)存储的是我们 new 来的对象即对象实例,不存放基本类型和对象引用。
(4)类加载器读取类文件后,一般会把类、方法、常量、变量及所有引用类型的真实对象放入堆中。
(5)由于创建了大量的对象,垃圾回收器主要工作在这块区域;
(6)会发生 OutOfMemoryError
5.2 结构图
5.3 新生区
5.3.1 概念
类的诞生,成长和死亡的地方
5.3.2 组成
(1)伊甸园区:所有对象都在伊甸园区new出来
(2)幸存0区和幸存1区:轻GC之后存下来的
5.4 老年区(养老区)
多次轻GC存活下来的对象放在老年区
8.5 永久区
jdk1.8后称为元空间:逻辑上存在,物理上不存在 ,因为:存储在本地磁盘内,不占用虚拟机内存
9.总结
(1)栈:基本类型的变量,对象的引用变量,实例对象的方法
(2)堆:存放由new创建的对象
(3)方法区:Class对象,static变量,常量池(常量)
备注: 默认情况下,JVM使用的最大内存为电脑总内存的四分之一,JVM使用的初始化内存为电脑总内存的六十四分之一.
随心所往,看见未来。Follow your heart,see night!
*欢迎点赞、关注、留言,收藏及转发,一起学习、交流!