jvm运行原理

虚拟机分类

classsic:中只提供了解释器(主导响应时间),如果jit编译器(主导性能)想要运行,解释器就不再工作了

exact:解决了编译器和解释器混合模式工作

hotspot:现在jdk中默认使用的虚拟机,上面两款虚拟机都没有方法区的概念,在服务器,桌面端,移动端和嵌入式都有应用

jrockit:专注于服务器端,不太关注响应时间,所以内部不包含解释器,只依靠即时编译器

j9:和hotspot一样在服务器,桌面端,移动端和嵌入式都有应用

Hotspot虚拟机在这里插入图片描述

java编译器hotspot虚拟机输入指令流主要是一个基于栈的指令集架构,具有跨平台性,指令集小,指令多的特点,但是执行性能要比寄存器架构差一些,寄存器架构耦合度高,可移植性差

jvm在对.class文件进行操作的时候,JIT编译器如果发现某些代码反复被执行,就会将其认定为热点代码,会将热点代码及时的编译成机器指令,然后把机器指令缓存起来

注:javav -p在class文件下查看反编译情况
jsp打印当前程序在执行中的进程

.java文件编译为.class文件的过程

javac解析过程:词法分析器->语法分析器->语法树/抽象语法树->语义分析器->注解抽象语法树->字节码生成器->生成Class文件

jvm执行流程图解:

在这里插入图片描述
在这里插入图片描述

1.7和1.8时运行时数据区的区别在这里插入图片描述

类加载子系统简介

加载阶段

将字节码数据从不同的数据源加载到jvm中,并映射为jvm认可的结构对象(class对象),加载阶段是用户参与的阶段,我们可以自定义类的加载器,去实现自己的类加载过程,

链接阶段

1.验证 jvm需要校验字节信息是否符合java虚拟机规范,为了防止恶意信息或者不合规信息对危害jvm的运行,验证阶段       有可能触发更多class的加载
2.准备 JVM 会为类成员变量(不包括实例变量)或者静态变量,静态代码块在方法区分配内存空间并且赋予默认初始值	  此侧重点在于分配所需要的内存空间,此阶段不会执行任何代码
3.解析 主要确认类、接口、属性和方法在类 run-time constant pool 的位置在这一步会将常量池中的符号引用替       换为直接引用

初始化阶段

这一步真正去执行类初始化的代码逻辑,包含静态字段赋值,执行静态代码块中的语句,只有在类加载完成后,创建对象等被动调用时,才会执行初始化阶段,类(接口)的初始化方法使用用户指定的值覆盖之前在准备阶段设定的初始值

注意:

1. 类加载器子系统,在加载阶段,将类加载器将.class文件加载到方法区中,在链接阶段,将class文件进行验证,准备,解析,将static变量或者代码块初始化,在被动调用的时候(比如new对象),首先在堆空间开辟一块内存空间,在对象头中存放方法区中对应的.class文件的地址,对象在堆空间中的地址存放在虚拟机栈中,从.class文件所在的方法区,再执行链接阶段,之后再初始化
2. 方法区的意义相当于java语法中的接口,永久代(1.7)和元空间(1.8)是方法的具体实现
3.1.8中,String常量池已经从方法区分离到了堆中,

Static修饰的变量和方法在jvm中的存储位置

在JDK8之前,静态成员(静态变量和静态方法)都是存储在方法区(永久代)中的静态区中(这里指类被加载后,静态成员的存储位置)。但在JDK8之后,永久代被移除了,取而代之的是元空间(metaspace)。但元空间中存储的主要是.class文件的元数据信息,静态成员的存储位置由方法区转到了堆内存(heap)中。
不过,不管是JDK8,还是更早的版本中,静态方法的执行(不仅仅是静态方法,还有普通的成员方法)都是在栈内存(stack)中进行的。每个线程都会在栈内存中开辟一个栈,在调用方法时,对应的方法都会在执行这个方法的线程的栈中创建一个“栈帧”,栈帧中保存了局部变量表(基本数据类型和对象引用)、操作数栈、动态连接和返回地址等信息。等到方法执行完毕,栈帧被销毁,对应的内存也将被释放

多线程使用Static方法产生的线程安全问题

在多线程中使用同一个静态方法时,每个线程使用各自的实例字段(instance field)的副本,而共享一个静态字段(static field)。所以说,如果该静态方法不去操作一个静态成员,只在方法内部使用实例字段(instance field),不会引起安全性问题。
但是,如果该静态方法操作了一个静态字段,则需要静态方法中采用互斥访问的方式进行安全处理。也就是说当某个静态类的静态方法使用了synchronized时实际上是对这个方法里的全局静态变量进行线程互斥保护。

类的被动调用触发条件

1. 创建类的实例
2. 访问某个类或接口的静态变量,或者对该静态变量赋值
3. 调用类的静态方法
4. 反射(比如:Class.forName(“com.atguigu.Test”)5. 初始化一个类的子类
6. Java虚拟机启动时被标明为启动类的类
7. JDK7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

双亲委派模型

当一个类加载器收到类加载任务时,立即将任务委派给它的父类加载器执行,直至委派给最顶层的启动类加载器为止,如果父类加载器无法加载委派给它的类时,将类加载任务退回给他的下一级加载器执行、

除了启动类加载器之外,每个类加载器拥有一个父类加载器,可以避免一个类被多个类加载器加载,造成的重复加载问题(JVM 区分不同类的方式不仅仅根据类名,双亲委派模型可以保证全限名指定的类,只被加载一次)

运行时数据区

虚拟机栈

栈帧
1. 首先栈是运行时的单位,而堆是存储的单位。
2. 即:栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放,放哪里

在这里插入图片描述

1.虚拟机栈是线程私有的,每一个线程都创建一个虚拟机栈,线程结束虚拟机栈销毁
2.虚拟机栈上有一个个的栈帧(线程执行中调用的每个方法都对应一个栈帧),栈帧中存储局部变量表、操作数栈、动态	链接、方法出口等信息
3.一个时间点上,只会有一个活动的栈帧,当前方法被调用时入栈,运行结束出栈,使前一个栈帧重新成为当前栈帧
局部变量表在这里插入图片描述
1.主要保存方法的参数以及局部的变量信息,表中的变量作用域是当前调用的函数,函数调用结束后,随着栈帧的销毁,	局部变量表也会随之销毁,释放空间
2.局部变量表所需的容量大小是在编译期确定下来的
3.存放的数据类型包含各类基本数据类型(byte,short,int,long,float,double,boolean,char)对象引用,以及returnaddress返回值类型
4.因为是私有的不存在线程安全的问题
5.局部变量表最基本的存储单元是slot(变量槽)
补充
基本数据类型的包装类型存放在运行时常量池中,例如:integer在-128~127存放在运行时常量池,大于127存放在内存的堆区

后续待更新。。。。。

相关推荐
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页