JVM基础
jvm:运行在操作系统上的假想的计算机
Java文件的编译和解释
JVM组成
GC回收算法
Java引用,GC垃圾收集器,OOM
类加载,类加载器
对象初始化
编译
JiT:即时编译
两大无关性
平台无关性:
每一台平台解释器不同,但是虚拟机相同,跨平台的原因。
一个程序对应一个虚拟机,多个程序对应多个虚拟机, 虚拟机之间数据不共享
什么是平台:操作系统及其硬件环境。跨平台即代码的运行不依赖于操作系统及其硬件环境。
c语言不是跨平台的,因为不同操作系统下的编译器编译代码得到不同文件,不能在其他操作系统上运行。即由Windows编译器得到的exe文件不能在Linux上运行
Java跨平台:因为编译后都得到class文件,可以在不同的操作系统上的解释器上执行。
并且注意:
不同的系统下有不同的JVM,所以JVM不是跨平台
JAVA依赖于JVM,JVM给JAVA提供了运行环境,所以JAVA是跨平台的。
程序执行的方式:
第一是编译执行:C它把源程序由特定平台的编译器一次性编译为平台相关的机器码,优点是执行速度快,缺点是无法跨平台;
第二是解释执行,如HTML,JavaScript,它使用特定的解释器,把代码一行行解释为机器码,类似于同声翻译,它的优点是可以跨平台,缺点是执行速度慢,暴露源程序;
第三种是先编译后解释,Java
Java源文件——编译器javac——class文件(字节码文件,二进制文件)
字节码文件——解释器(jvm)java——机器码文件
关于JVM的解释过程:
参考资料
第一种:解释执行:即逐条将字节码翻译成机器码并执行 [在解释执行过程中,每当为 Java 方法分配栈桢时,Java 虚拟机往往需要开辟一块额外的空间作为操作数栈,来存放计算的操作数以及返回结果]
第二种:即时编译(Just-In-Time compilation,JIT),利用JIT即时编译器,一个方法中包含的所有字节码编译成机器码后再执行,并且会储存下次再运行时无需编译。
前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。
注意即时编译和编译是不同的
语言无关性:JVM不仅能运行Java文件,还能运行入JRuby,Jython,Scala等。
关键是class文件,JVM要求class文件必须实现一系列规范,但是并没有要求必须Java编译,其他语言也可以编译成class文件
线程
jvm线程和原生操作系统线程具有直接映射关系
无论是Timed Waiting ,Waiting还是Blocked,对应的都是操作系统线程的waiting(等待)状态。
而Runnable状态,则对应了操作系统中的ready和running状态。
虚拟机中的线程状态,不反应任何操作系统线程状态
虚拟机线程 (VM thread)
这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-the- world 垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。
周期性任务线程
这线程中断事件,用来调度周期性操作的执行。
GC 线程
这些线程支持 JVM 中不同的垃圾回收活动。
编译器线程
这些线程在运行时将字节码动态编译成本地平台相关的机器码。
信号分发线程
这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理
jvm内存区域
执行引擎:编译器,垃圾收集
数据:
共享数据区:方法区,堆
私有数据区:虚拟机栈,本地方法栈,程序计数器
本地库接口 用于连接本地方法库
线程私有数据生命周期和操作系统线程相同
线程共享区域和虚拟机生命周期相同
私有数据
程序计数器:
是当前线程所执行的字节码的行号指示器。
在解释执行中,因为是从头开始的。如果存在多线程并发执行,但线程被挂起时,需要记录正在执行的代码,保存在程序计数器中。
如果是执行Java代码,那么就是正在执行的虚拟机字节码的地址,如果是Naive方法,那么为空
虚拟机栈:
由栈帧组成,每一个方法对应一个栈帧,栈帧随着方法创建而创建,随着方法销毁而销毁。
栈帧由局部变量表、操作数栈、动态链接、方法出口
1、局部变量表:是一组变量值的存储空间。
包括方法参数
方法内的变量:基本数据类型,对象的引用reference不是对象本身
returnAddress类型(一条字节码执行的地址,异常处理,现很少使用)
是以32位的slot进行存储,如果遇到64位则高位补齐的2个slot。以索引表的形式访问
2、操作数栈:常称为操作数栈,是一个后入先出栈。
用途:刚开始为空,执行某些用途时,会将局部变量表中的数据拷贝到操作数栈
算术运算
调用其他的方法进行参数传递
在概念模型中,两个栈帧是相互独立的。但是大多数虚拟机的实现都会进行优化,令两个栈帧出现一部分重叠。令下面的部分操作数栈与上面的局部变量表重叠在一块,这样在方法调用的时候可以共用一部分数据,无需进行额外的参数复制传递。
3、动态连接:栈帧持有一个指向 方法区常量池 中所属方法的引用明确调用的是哪一个方法
4、方法返回地址:方法的返回分为两种情况
一种是正常退出,退出后会根据方法的定义来决定是否要传返回值给上层的调用者
一种是异常导致的方法结束,这种情况是不会传返回值给上层的调用方法.
不过无论是那种方式的方法结束,在退出当前方法时都会跳转到当前方法被调用的位置
本地方法栈:Native 方法服务
共享数据:
堆:创建的对象本身和数组都保存在 Java 堆内存中
数组同样是对象 int a[]=new int [3] 所以一切new的对象本身都在堆中
对象又包括成员变量和方法,成员变量在堆中
本地方法区:我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息class、常量(final ,“abc”)、静态变量(static)、即时编译器编译后的代码等数据
注意常量和静态变量在jdk1.7后放入堆中
首先明确,常量池在解释之前,准备阶段就已经初始化
注意常量池中有基本数据类型的值:
对于int,都会放入常量池,但是如果int值过于简单如1,不会放入常量池而是通过JVM字节码将值赋值给相应字段
对于Integer,利用了享元技术,值在(-128,127)会放入常量池,而如果超出范围,会自动装箱从而保存在堆中。
参考资料
符号引用其实是从字节码角度来标识类、方法、字段。字节码只有加载到内存中才能运行,加载到内存中,就是内存寻址了。
符号引用:
在class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法,类就如果不经过运行期转换的话无法得到真正的内存入口地址,只能通过字节码的形式保存也就是符号引用。
符号引用转换为直接引用:
加载验证准备解释中的解释的时候:静态链接
运行的时候,通过虚拟机栈的栈帧中的动态链接:动态链接
参考资料
运行时常量池:存在于内存中,是静态常量池被加载这里的加载时解释的一部分到内存之后的版本
class常量池和运行时常量池的区别:
1:先后区别
2:JVM队静态常量池有严格的规定,对运行时常量池没有严格规定
3: