JVM

2019-05-05  本文已影响0人  CHENpq

JVM

通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。

The compiler compiles the Java  .java file into a Java .class file, then that .class file is input into theJVM, which Loads and executes the class file.

JVM will take the .class file and converting into machinecode instruction

JRE JDK JVM关系

JRE(Java Runtime Environment, Java运行环境)是Java平台,所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。

JDK(Java Development Kit,Java开发工具包)是用来编译、调试Java程序的开发工具包。包括Java工具(javac/java/jdb等)和Java基础的类库(java API )。

JVM(Java Virtual Machine, Java虚拟机)是JRE的一部分。JVM主要工作是解释自己的指令集(即字节码)并映射到本地的CPU指令集和OS的系统调用。Java语言是跨平台运行的,不同的操作系统会有不同的JVM映射规则,使之与操作系统无关,完成跨平台性。

总结:使用JDK(调用JAVA API)开发JAVA程序后,通过JDK中的编译程序(javac)将Java程序编译为Java字节码,在JRE上运行这些字节码,JVM会解析并映射到真实操作系统的CPU指令集和OS的系统调用。

JVM包含三个结构:

类加载器Class Loader:用于装载.class文件

运行时数据区Runtime Data Area:方法区、堆、java栈、pc寄存器、本地方法栈

执行引擎Execution Engine:执行字节码或者本地方法(不是native方法)

JVM生命周期

Java 实例对应一个独立运行的 Java 程序(进程级别

JVM执行引擎实例则对应了属于用户运行程序线程(线程级别

启动:

启动一个Java程序,一个JVM实例就产生。拥有public static void main(String[] args)函数的class可以作为JVM实例运行的起点。

运行:

main()作为程序初始线程的起点,任何其他线程均可由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM使用,程序可以指定创建的线程为守护线程。

其实在本质上,用户线程 UserThread 和守护线程 DaemonThread 并没有太大区别,唯一的区别就是会影响虚拟机的退出(程序的终止)。当jvm中只剩下守护线程时,虚拟机会退出,及程序终止;而当jvm中至少拥有一个用户线程时,jvm都不会退出。

消亡:

main()作为程序初始线程的起点,任何其他线程均可由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM使用,程序可以指定创建的线程为守护线程。

Java类加载器

类的加载是将类的.class文件中的二进制数据读入到内存中

–  类加载器通过类的全路径限定名读取类的二进制字节流

–  将二进制字节流代表的类结构转化到运行时数据区的方法区

–  在内存中生成代表这个类的java.lang.Class实例(不是这个类 的实例)

静态变量是在类 linking 时赋默认值, initialize 时初始化;成员变量在实例化时初始化。

类加载器在 runtime (第一次用到类)时加载类, 而不是 compile time.

装载 Loading:

负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名、ClassLoader完成类的加载。

链接 Linking:

负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口。

校验后,JVM初始化类中的静态变量,并将其赋值为默认值。

初始化 Initializing:

负责执行类中的静态初始化代码、构造器代码以及静态属性的初始化,以下四种情况初始化过程会被触发。

new()

反射调用了类中的方法 obj=clazz.getInstance();

子类调用了初始化

JVM启动过程中,指定的初始化类

JVM类加载顺序(CLassLoader的Loading步骤加载)

Bootstrap Class Loader(c++编写 最顶层 加载最核心类)

Extension Class Loader

JVM用此classloader来加载扩展功能的一些jar包

System Class Loader

委派模式

当JVM加载一个类的时候,下层的加载器会将任务给上一层类加载器,上一层加载检查它的命名空间中是否已经加载这个类,如果已经加载,直接使用这个类。如果没有加载,继续往上委托直到顶部。检查之后,按照相反的顺序进行加载。如果Bootstrap加载器不到这个类,则往下委托,直到找到这个类。一个类可以被不同的类加载器加载。

可见性限制:下层的加载器能够看到上层加载器中的类,反之则不行,委派只能从下到上

不允许卸载类:类加载器可以加载一个类,但不能够卸载一个类。但是类加载器可以被创建或者删除。

Java运行时数据区

方法区 Method Area:(进程)

方法区域存放所加载类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息

方法区域也是全局共享的,在一定条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,就会抛出OutOfMemory 的错误信息。

堆 Heap:(进程)

可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,导致new对象的开销比较大。

Sun Hotspot JVM为了提升对象内存分配的效率,对于所有创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样的,但如果对象过大的话则仍然要直接使用堆空间分配。

jvm栈 JVM Stack:(线程)

JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放当前线程中局部基本类型的变量(Java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆的地址

pc寄存器 PC Register:(线程)

正在执行的字节码地址,当线程去执行Native方法时,程序计数器则为Undefined

本地方法栈 Native Method Stacks:(线程)

JVM采用本地方法堆来支持native方法的执行,此区域用于存储每个native方法调用的状态。

JVM垃圾回收

不同的对象引用类型,GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:

强引用:

默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用时, GC时才会被回收)

软引用:

软引用是Java中提供的一种比较适合于缓存场景的应用(只有内存不够的情况下才会被GC)

弱引用:

在GC时一定会被GC回收。

虚引用:

虚引用只是用来得知对象是否被GC。

执行引擎Execution Engine:

把byte code转化成machine code

配置环境变量

exportJAVA_HOME=/usr/java/jdk1.8.0_201

exportCLASSPATH=$JAVA_HOME/lib/

exportPATH=$PATH:$JAVA_HOME/bin

上一篇 下一篇

猜你喜欢

热点阅读