- Java虚拟机是整个Java平台的基石,是Java技术用以实现硬件无关与操作系统无关的关键部分,是Java语言生成出极小体积的编译代码的运行平台,是保障用户机器免于恶意代码损害的屏障。
- Java虚拟机可以看做一 台抽象的计算机。如同真实的计算机那样,它有自己的指令集以及各种运行时内存区域。使用虚拟机来实现一门程序设计语言是相当常见的,业界中流传最为久远的虚拟机可能是UCSDPascal的P一Code虚拟机。
- Java虚拟机并不局限于特定的实现技术、主机硬件和操作系统。Java虚拟机也不局限于特定的代码执行方式,它虽然不强求使用解释器来执行程序,但是也可以通过把自己的指令集编译为实际CPU的指令来实现。它可以通过微代码( microcode)来实现,甚至可以直接在CPU中实现。
- Java虚拟机与Java语言并没有必然的联系,它只与特定的二进制文件格式一class文件格式所关联。class文件包含了Java虚拟机指令集(或者称为字节码( bytecode))和符号表,以及其他一些辅助信息。
- 基于安全方面的考虑,Java虚拟机在class文件中施加了许多强制性的语法和结构化约束,凡是能用class文件正确表达出来的编程语言,都可以放在Java虚拟机里面执行。由于它是一个通用的、机器无关的执行平台,所以其他语言的实现者都可以考虑将Java虚拟机作为那些语言的交付媒介。
JVM三大核心部分:类加载器,运行时数据区,执行引擎
三大核心执行顺序:
类加载器
将class文件加载解析,载入JVM中,包括:分配内存,静态数据,类信息等.
运行时数据
划分为五大组件:堆,方法区,虚拟机栈,本地方法栈,程序计数器.
执行引擎
分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐段执行。
1.JVM启动流程
2.类的加载过程
虚拟机将描述类的数据从Class文件加载到内存,并对数据进行校验、准备、解析和初始化,最终就会形成可以被虚拟机使用的Java类型,这就是一个虚拟机的类加载机制。Java中的类是动态加载的,只有在运行期间使用到该类的时候,才会将该类加载到内存中,Java依赖于运行期动态加载和动态链接来实现类的动态使用。
一个类的生命周期:加载,验证,准备,解析,初始化,卸载.
其中加载,验证,准备,初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的动态绑定。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或**另一个阶段。
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与(支持Java虚拟机提供的引导类加载器和用户自定义的类加载器)。
-
链接:将java类的二进制代码合并到JVM的运行状态之中的过程(涉及内存分配)
-
验证(verification)阶段用于确保类或接口的二进制表示在结构上是正确的。验证过程可能会导致某些额外的类和接口被加载进来,但不一定会.导致它们也需要验证或准备。
-
准备(preparation) 阶段的任务是创建类或接口的静态字段(内存分配的仅包括类变量(static)),并用默认值(如0、0L、null、false等)初始化这些字段。这个阶段不会执行任何的虚拟机字节码指令。在初始化阶段会有显式的初始化器来初始化这些静态字段,所以准备阶段不做这些事情。
-
解析(resolution) 是根据运行时常量池里的符号引用来动态决定具体值的过程。
-
-
初始化:类初始化阶段是类加载的最后一步,前面的类加载过程中,除了在加载阶段用户用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说字节码)。这里所有的静态变量会被赋初始值, 并且静态块将被执行。对于接口和类来说,就是执行它们的初始化方法.
有且只有以下五种情况才会初始化
① 使用new关键字实例化对象、访问或者设置一个类的静态字段(被final修饰、编译器优化时已经放入常量池的例外)、调用类方法,都会初始化该静态字段或者静态方法所在的类;
② 初始化类的时候,如果其父类没有被初始化过,则要先触发其父类初始化;
③ 使用java.lang.reflect包的方法进行反射调用的时候,如果类没有被初始化,则要先初始化;
④ 虚拟机启动时,用户会先初始化要执行的主类(含有main);
⑤ jdk 1.7后,如果java.lang.invoke.MethodHandle的实例最后对应的解析结果是 REF_getStatic、REF_putStatic、REF_invokeStatic方法句柄,并且这个方法所在类没有初始化,则先初始化;
3.运行时数据
JDK 1.6,字符串常量池位于永久代的运行时常量池中;
JDK 1.7,字符串常量池从永久代剥离,放入了堆中;
JDK 1.8,元空间取代了永久代,并且放入了本地内存(Native memory)中。
-
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完。
程序计数器是唯不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。 -
Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型。Java虚拟机栈是由一个个栈帧组成,线程在执行一个方法时,便会向栈中放入一个栈帧,每个栈帧中都拥有局部变量表、操作数栈、动态链接、方法出口信息。局部变量表主要存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
-
本地方法栈则为虚拟机使用到的 Native 方法服务,和虚拟机栈所发挥的作用非常相似,区别是: 虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务.
-
堆是Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存,是垃圾收集器管理的主要区域.
-
方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
-
运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)
4.执行引擎
分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐段执行。
- 解释器: 解释器能快速的解释字节码,但执行却很慢。 解释器的缺点就是,当一个方法被调用多次,每次都需要重新解释。
- 编译器:JIT编译器消除了解释器的缺点。执行引擎利用解释器转换字节码,但如果是重复的代码则使用JIT编译器将全部字节码编译成本机代码。
5.GC
java GC泛指java的垃圾回收机制,该机制是java与C/C++的主要区别之一,我们在日常写java代码的时候,一般都不需要编写内存回收或者垃圾清理的代码,也不需要像C/C++那样做类似delete/free的操作。
Java堆是GC回收的“重点区域”。堆中基本存放着所有对象实例,gc进行回收前,第一件事就是确认哪些对象存活,哪些死去[即不可能再被引用]
为了高效的回收,jvm将堆分为三个区域
1.新生代(Young Generation)NewSize和MaxNewSize分别可以控制年轻代的初始大小和最大的大小
2.老年代(Old Generation)
3.永久代(Permanent Generation)【1.8以后采用元空间,就不在堆中了】
1.引用计数算法
早期判断对象是否存活大多都是以这种算法,这种算法判断很简单,简单来说就是给对象添加一个引用计数器,每当对象被引用一次就加1,引用失效时就减1。当为0的时候就判断对象不会再被引用。
优点:实现简单效率高,被广泛使用与如python何游戏脚本语言上。
缺点:难以解决循环引用的问题,就是假如两个对象互相引用已经不会再被其它其它引用,导致一直不会为0就无法进行回收。
2.可达性分析算法
目前主流的商用语言[如java、c#]采用的是可达性分析算法判断对象是否存活。这个算法有效解决了循环利用的弊端。
它的基本思路是通过一个称为“GC Roots”的对象为起始点,搜索所经过的路径称为引用链,当一个对象到GC Roots没有任何引用跟它连接则证明对象是不可用的。
三大垃圾收集算法
1.标记/清除算法【最基础】
2.复制算法
3.标记/整理算法
jvm采用分代收集算法对不同区域采用不同的回收算法。
6.JVM退出条件
- 某线程调用Runtime类或System类的exit方法,或Runtime类的halt方法,并且Java安全管理器也允许这次exit或halt操作
- JNI规范描述了用JNI Invocation API来加载或卸载Java虚拟机时,Java虚拟机的退出情况.
参考列表:
java虚拟机规范(Java SE 8版)-机械工业出版社-2015/6版
JVM主要包含三大核心部分:运行时数据区,类加载器和执行引擎。
JVM核心之JVM运行和类加载全过程
java文件运行全流程及JVM类加载机制
Java内存管理-初始JVM和JVM启动流程(二)
Java内存管理-JVM内存模型以及JDK7和JDK8内存模型对比总结(三)
深入理解Java之jvm启动流程
JVM内存模型详解
JVM内存模型和面试题解析
深入理解JVM-内存模型(jmm)和GC