Java Virtual Machine简写JVM。通过针对不同系统、硬件平台实现统一的应用接口,是Java语言跨平台的根本。了解JVM的原理可以帮助我们写出更健壮的代码,解决由JVM层面的造成的错误、性能的短板等问题。
1. JVM JRE JDK的区别
JVM是抽象的计算机,为二进制字节码执行及其执行环境提供了一套规范。JVM只能识别理解编译之后的字节码文件(.class),通过JVM的执行引擎将字节码转换成可被执行的机器语言。
1.1 JVM
三个主要部分:
1. 类加载子系统: 加载需要的class jar文件、连接、初始化
2. 运行时数据区域
3. 执行引擎:字节码解析、执行
JVM = 类加载子系统 + 运行时数据区域 + 执行引擎
1.2 JRE
Java Runtime Environment(Java运行环境)通常是虚拟机的实现。它由虚拟机、class libraries(rt.jar)和其他的用来支持执行的文件(java javaw等),JVM运行应用程序的时候使用JRE提供的类库。但是并不包含开发者工具,例如:编译(javac)、反汇编(javap)、调试(jdb)等。
JRE = JVM + Libraries
1.3 JDK
Java Developer Kit包含了Java开发者工具和运行应用程序的JRE。
JDK = JRE + Development Tools
2. JVM概览
2.1 架构图
图片来自维基百科 引自参考文献22.2 各个区域的作用
2.2.1 方法区:
方法区(Method Area)是堆的一个逻辑部分(不同虚拟机表现可能不一致),该区域主要存放了虚拟机类加
载子系统载入的类信息、常量、即时编译(JIT Compiler)后的代码等数据。
Class文件中的常量池:主要存放两大类常量字面量和符号引用。字面量比较接近于Java语言层面的常量概
念,如文本字符串、声明文final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常
量:
1. 类和接口的全限定名
2. 字段的名称和描述符
3. 方法的名称和描述符
运行时常量池:运行时常量池相对于Class文件中的常量池具备动态性,Java语言并不要求常量一定只有在
编译器才能产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也
可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
2.2.2 堆区
所有的对象实例和数组都分配到堆区(Heap Area)中,和方法区一样也是多线程共享的区域。
并不是所有的对象一定会在堆上进行分配,对象不在堆上分配有以下两种情况:
1. TLAB(Thread local allocation buffer)和栈上分配。JVM在新生代(Eden Space)中开辟了一小块区域,由线程私有,称作TLAB,默认设定为占用Eden Space的1%。在Java程序中有很多对象都是小对象通常JVM会有限分配在TLAB上,并且TLAB上的分配由于是线程私有所以没有锁开销。因此在实践中分配多个小对象的效率通常比分配一个大对象的效率要高。
2. 栈上分配。JVM在Server模式下的逃逸分析可以分析出某个对象是否永远只在某个方法、线程的范围内
,并没有“逃逸”出这个范围,逃逸分析的结果就是对于某些逃逸对象可以直接在栈上分配,由于该对象一定
是局部的,所以栈上分配不会有线程安全问题。
2.2.3 虚拟机栈
Java虚拟机栈(Java Virtual Machine Stacks)是线程私有的区域,它的生命周期和线程相同。虚拟机
栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于
存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应
着一个栈帧在虚拟机栈中入栈到出栈的过程。
2.2.4 本地方法栈
本地方法栈和虚拟机栈类似作用类似,虚拟机栈是服务于Java字节码执行,而本地方法栈服务于本地方法
执行(一般为C语言),同样对于每个线程都会创建本地方法栈。
2.2.5 程序计数器
每个线程都有一个单独的程序计数器,指向当前指令的地址,一旦当前指令被执行计数器就会更新到下一条
指令的地址。
参考文献
. difference-between-jdk-jre-and-jvm
. 《深入理解Java虚拟机》
网友评论