Java virtual Machine : write once,run anywhere !
1、概述:
Hotspot是jvm规范的一种实现,java是运行class文件,javac将Java文件编译成class文件(十六进制文件,源文件并不一定要java语言编写),class文件中每两个十六进制位代表U1
magic(cafe babe) :标志class文件的正确格式,正确的class文件格式才可运行在jvm中。jvm只识别十六进制的文件,这文件不一定要从java源码编译过来,可以是其他语言通过其他编译方式,形成class文件,就可以在jvm上运行
2、类加载机制:(双亲委派)
2.1概述
-
java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制。
-
Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这里就是我们经常能见到的Class类。
-
装载:找到位置
-
链接::
验证是否为正确的class文件
准备给类的静态变量分配存储空间;
解析(将符号引用转为直接引用,就是将class文件中的十六进制的无意义符号与实际物理内存地址对应起来) -
初始化:对类的静态变量,静态代码块执行初始化操作
2.2加载器类别
启动类加载器:这个加载器不是一个Java类,而是由底层的c++实现,负责将存放在JAVA_HOME下lib目录中的类库,比如rt.jar。因此,启动类加载器不属于Java类库,无法被Java程序直接引用
扩展类加载器:由sun.misc.Launcher$ExtClassLoader实现,负责加载JAVA_HOME下libext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
应用类加载器:由sun.misc.Launcher$AppClassLoader实现的。由于这个类加载器是ClassLoader中的getSystemClassLoader方法的返回值,所以也叫系统类加载器。它负责加载用户类路径上所指定的类库,可以被直接使用。如果未自定义类加载器,默认为该类加载器。
2.3、双亲委派机制:
-
解决类重复加载以及核心类安全问题,不同的加载器去加载不同位置。为什么说是核心类安全问题?若由黑客写了string类,则通过一个应用加载类加载进jvm的话,可能会引发安全问题,如黑客编写的string类里含有病毒。双亲委派模型:当一个类加载器接收到类加载请求时,会先请求其父类加载器加载,依次递归,当父类加载器无法找到该类时(根据类的全限定名称),子类加载器才会尝试去加载。
-
双亲委派模型是为了保证Java核心库的类型安全。所有Java应用都至少需要引用java.lang.Object类,在运行时这个类需要被加载到Java虚拟机中。如果该加载过程由自定义类加载器来完成,可能就会存在多个版本的java.lang.Object类,而且这些类之间是不兼容的。
-
通过双亲委派模型,对于Java核心库的类的加载工作由启动类加载器来统一完成,保证了Java应用所使用的都是同一个版本的Java核心库的类,是互相兼容的。
3、执行引擎
3.1、概述:
- 虚拟机是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的。而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。为什么要指定指令集?是为了可以经由执行引擎翻译成机器指令,这便是说jvm的执行引擎可以执行哪些不被硬件直接支持的指令集格式,其实jvm执行引擎就做了一个翻译指令的工作。本质上还是由本地机器执行
- JVM的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被JVM锁识别的字节码指令、符号表和其他辅助信息。那么,如果想让一个Java程序运行起来、执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者。
3.2、执行引擎体系:
1、解释编译器(interpreter):
当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。速度慢,可以跨平台保证
AOT:adead of time:代码执行之前编译
graal vm:编译器
2、JIT(Just in Time):即时编译器
编译执行,也叫后端编译器,javac是将java源码编译成class文件,是前端编译器,速度快,无法跨平台.为了解决前端编译器无法跨平台,解释编译器执行慢的问题,JVM平台支持一种叫作即时编译的技术。即时编译的目的是避免函数这种热点代码被解释执行,而是将整个函数体编译成为机器码,每次函数执行时,只执行编译后的机器码即可,这种方式可以使执行效率大幅度提升。
作用:class文件—》翻译(解释翻译或编译翻译)------》不同架构下的010101…
3、垃圾回收机制
标记清除垃圾回收:将要回收的垃圾进行标记,然后在适当时机进行回收,但会导致空间碎片,为了解决空间碎片,进一步优化出复制垃圾回收
复制整理垃圾回收:将空间一分为二,左边是将要回收的垃圾进行标记,右边是没有使用的空间,当进行回收时会将左边标记的垃圾复制到右边连续的区域,然后将左边空间全部清空
标记整理:将没有被回收的进行整理
对应垃圾回收算法的落地是垃圾回收器
Minor gc=young gc
Major gc=old gc
full gc==(matespace gc)+younggc+old gc
调优原则:
尽量不要发生gc
即使发生gc,也要尽可能先考虑yong gc,
一般old gc会伴随yong gc
full gc要尽可能避免
4、jvm引擎执行过程
- 当虛拟机启动的时候,解释器可以首先发挥作用,而不必等待即时编译器全部编译完成再执行,这样可以省去许多不必要的编译时间。
- 并且随着程序运行时间的推移,即时编译器逐渐发挥作用,根据热点探测功能,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率。
-Xint: 完全采用解释器模式执行程序;
-Xcomp: 完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行。
-Xmixed:采用解释器+即时编译器的混合模式共同执行程序。
4、运行数据区
java内存模型,jvm内存模型,jvm运行数据区是什么关系?
4.1、jvm运行时数据区:展示是程序运行时候的数据状态
(1)静:数据存储(jvm内存模型)
- heap(堆):对象或数组,成员变量(因为创建的对象在堆中,进一步使用成员变量)
- mathod area(方法区):类的元数据信息(描述信息)、静态变量,常量(运行时常量池),即都是编译之后的内容
堆和方法区:在java虚拟机中只有一个(java进程中),被所有线程共享
(2)动:方法运行(线程工作内存)
- java虚拟机栈:生命周期和线程绑定在一起,用于执行java方法
局部变量表,操作数栈,动态链接(代码执行到时才确定),方法返回地址 - 本地方法栈:用于执行本地方法(c或C++写的语言),如native方法
- pc register:用于记录正在执行的方法
这三块都是线程私有,安全
动态链接是链接到本地方法栈,栈是存储数据的,线程是操作栈,一个栈帧表示的就是方法的执行
4.2、jvm内存模型:对于运行时数据区(堆和方法区)的一个物理落地,分为方法区【metaspace(元空间),pem(永久代)】,堆【young(eden,s0,s1),old】。为什么没有本地方法栈,虚拟机栈,pc计数器呢?因为jvm内存模型一种对内存的模型用来存储数据,并不只是在运行时才有的,垃圾回收器工作的地方。而另外三个是线程私有执行完就销毁,不需要垃圾回收器工作,所有从这个角度看jvm的内存模型只有方法区和堆
4.3、java内存模型(JMM):java中线程和内存数据交互的一种方式,定义了线程和主内存之间的抽象关系。JVM内存模型指的是JVM的内存分区;而Java内存模式是一种虚拟机规范。JMM规范了Java虚拟机与计算机内存是如何协同工作的
5、Java内存模型和硬件内存架构关系
- 每个线程都有一个私有的本地内存(JMM的抽象概念),里面存放了该线程读/写共享变量的拷贝副本;
- 低层次角度看,主内存就是硬件内存,为了获取更高的运行速度,虚拟机及硬件系统会将工作内存优先存储与寄存器和高速缓存中;
- JMM中线程的工作内存(本地内存),是CPU的寄存器和高速缓存的抽象描述;而JVM的静态存储模型(JVM内存模型)只是一种对内存的物理划分而已,它只局限于内存。
总结:
jvm运行时数据区展示是程序运行时候的数据状态,,他真正的内存划分是jvm内存模型,那么Java虚拟机栈,pc计数器,本地方法栈去哪了呢?这便是线程的本地内存也叫工作内存是一种对是CPU的寄存器和高速缓存的抽象描述。此时便来到了java内存模型