JVM 微解1

44 篇文章 6 订阅

知识储备:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值