JVM 微解1

来源:互联网 发布:淘宝网中年雪纺衫 编辑:程序博客网 时间:2024/06/10 21:19

知识储备:

JVM和普通虚拟机

vmvare,visualbox虚拟机:完整的一个能够提供虚拟主机的PC,所以我们需要在上边安装操作系统,是通过使用操作系统软件模拟物理CPU的指令集
jvm:程序自己的独立运行环境,比如说:对战,寄存器,虚拟硬件架构,Java字节码指令集等

JVM/JDK/JRE关系

JDK : Java Development ToolKit(Java开发工具包)。JDK是整个JAVA的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。

JRE:Java Runtime Enviromental(java运行时环境)。也就是我们说的JAVA平台,所有的Java程序都要在JRE下才能运行。包括JVM和JAVA核心类库和支持文件。与JDK相比,它不包含开发工具——编译器、调试器和其它工具。

JVM:是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

这里写图片描述

JVM产品有哪些

HotSpot VM、J9 VM、Zing VM

JAVA文件加载到JVM的运行流程:

.java文件编译为.class文件,.class文件通过JVM运行。
这里写图片描述
类加载器用的是双亲委派模型;
类加载过程:

1.加载(Load)

取得类的二进制字节流,通过类的全限定名称(包名+类名) 把二进制字节流中静态存储结构转化为方法区数据结构在内存中生成代表这个类的java.lang.Class对象,放到堆中

2.链接(Link)

    2.1验证(检测)    验证的目的是为了确保Class文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。        文件格式的验证,            是否是class文件,当前class文件是否支持当前的虚拟机等等        元数据验证            对类的元数据信息进行语义校验(其实就是对类中的各数据类型进行语法校验),保证不存在不符合Java语法规范的元数据信息。        字节码验证            该阶段验证的主要工作是进行数据流和控制流分析,对类的方法体进行校验分析,以保证被校验的类的方法在运行时不会做出危害虚拟机安全的行为。        符号(其实就是关键字)引用验证            对方法的引用是否合法,转成直接引用     **2.2准备**             为类的静态变量分配内存,并初始化默认值            int: 0            long: 0L            boolean:false    **2.3解析**         在一个Java类中会包含对其它类或接口的形式引用,包括它的父类、所实现的接口、方法的形式参数和返回值的Java类等。解析的过程就是确保这些被引用的类能被正确的找到。解析的过程可能会导致其它的Java类被加载。        常量池中的符号引用替换为直接引用(符号引用,可以理解为一个占位符)。        例如person.fun() 在编译期间, 并不知道Person类的实际内存地址,因此只能使用符号来表示:            例如com.tu.demo.Person,表示一个符号。当然jvm实际中是使用似于CONSTANT_Class_info的常量来表示的            解析阶段,就是把这些符号引用,转换成一个个的指针

3.初始化(Initialize)

    初始化阶段,才真正开始执行类中定义的java程序代码(或者说是字节码)    通俗的讲,初始阶段是执行类构造器<clinit>()方法的过程。    <clinit>()不是构造器,在构造器之前执行       如果这个类还没有被加载和链接,那就先加载和链接    如果类存在直接的父类,先初始化直接父类    如果类中存在初始化语句,那就依次执行初始化语句主动引用:        new,创建类的实例对象        反射(和new是一样的,反射在init的时候调用的就是new)        初始化子类必需要先要初始化父类        调用类的静态方法        访问类中的静态变量或者给静态变量赋值        jvm启动的时候的启动类必须要先初始化(常量不会对类的初始化产生影响)被动引用:    除上述之外,所有引用类的方式都不会出发初始化,称为被动引用。类的构造器<clint>:    只能写不能读    (可以读已经定义的, 未定义的不能读)**需要写代码举例子**    例如:        1.通过子类引用父类的静态字段,不会导致子类的初始化        2.通过数组定义来引用类,不会出发此类的初始化        3.常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,两个类在编译之后不再有联系,因此不会触发类的初始化。

JVM运行结构:

class字节码文件通过类装载器子系统进行装载,
类的加载只会加载一次
这里写图片描述

JVM运行时数据区域:

这里写图片描述
计数器:

当前线程执行的字节码的行号指示器,通过改变此指示器来选取下一个需要执行的字节码指令特征    在线程创建时创建    每个线程拥有一个    指向下一条指令的地址

方法区

线程共享存储:    类信息    常量    静态变量【jdk1.7之前存放在方法区】    方法字节码   

VM栈/本地方法栈

局部变量-基础类型【jdk1.7String放在堆】,如果是引用类型,则引用被存在栈上,引用指向的对象放在堆线程私有方法在执行时会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程局部变量表所需的内存空间在编译期间完成分配,而且分配多大的局部变量空间是完全确定的,在方法运行期间不会改变其大小出栈后空间释放

Heap

线程共享存储对象或数组(new 出来的对象)堆划分非静态变量静态变量【jdk1.7及之后,静态变量和非静态变量(普通变量) 都存在堆中】

这里写图片描述

注意:

jdk1.7之前,静态变量存放在方法区中,非静态变量存放在堆中(在实例化堆的时候)jdk1.7及之后,静态变量和非静态变量(普通变量) 都存在堆中局部变量如果是基础类型,则在栈上(jdk1.7String放在堆),如果是引用类型,则引用被存在栈上,引用指向的对象放在堆

JVM内存模型:

valitale

线程间可见

syhcronize语义:

串行

指令重排

现象:同一个线程中只有赋值操作。 比如一下代码,a 和 sign的执行顺序没有一定的顺序,就会发生指令重排         
int a = 0;boolean sign = false;new Thread(new Runable(){    a   +=  1;    sign = true;});
解决方案:synchroize

参考资料:
http://java-mzd.iteye.com/blog/838514
https://zhuanlan.zhihu.com/p/25228545
http://blog.csdn.net/songkai320/article/details/51819046
http://blog.csdn.net/qq_25235807/article/details/61920877
https://www.zhihu.com/question/29125656
https://my.oschina.net/wanghongkai/blog/507649(Java类加载机制介绍比较详细且到位)
http://www.infoq.com/cn/articles/cf-Java-class-loader#idp_register