什么是JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具
体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
Java为什么是"平台无关的编程语言"
Java虚拟机是一个可以执行Java字节码的虚拟机进程。
Java源文件被编译成能被Java虚拟机执行的字节码文件(.class文件)。
Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。
Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
JRE/JDK/JVM是什么关系
JRE是Java运行环境,即(Java Runtime Environment),也就是Java平台。所有的Java程序都要在JRE下才能运行。
JDK是开发工具包,即(Java Development Kit),它是程序开发者用来编译、调试Java程序,它也是Java
程序,也需要JRE才能运行。
JVM是Java虚拟机,即(Java Virual Machine),它是JRE的一部分,一个虚构出来的计算机,它支持跨平台。
小结
Java虚拟机成为jvm,它是分离了一部分真实电脑的资源。我们写的Java程序都是在JVM上运行的,不是在真实的计算机上运行。
程序员写程序 -> JVM ->真实的计算机。
JVM解决了各操作系统兼容性问题。
目前最新版本是jdk22,使用版本一般是JDK8,JDK只有3个版本是商用免费的,即8、11、17。
JDK:开发和运行 ,其中包含了JRE,可以将.java文件 字节 为.class文件。
JRE:运行环境,其中包含JVM,运行.class文件。
JVM:通过运行.class文件去调用底层操作系统。
三种JVM :
① Sun公司的HotSpot;
② BEA公司的JRockit;
③ IBM公司的J9 JVM;
在JDK1.7及其以前我们所使用的都是Sun公司的HotSpot,但由于Sun公司和BEA公司都被oracle收购,
jdk1.8将采用Sun公司的HotSpot和BEA公司的JRockit两个JVM中精华形成jdk1.8的JVM。
JVM原理
JVM是Java核心和基础,在Java编译器和os平台之间的虚拟处理器,可以在上面执行Java的字节码程序。Java编译器主要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台的机器码,通过特定平台运行。
JVM体系结构
类加载器:加载class文件。
执行引擎:执行字节码或者执行本地方法。
运行时数据区:包括方法区、堆、Java栈、PC寄存器、本地方法栈。
JVM运行时数据区
1.类加载器(ClassLoader):在JVM启动时或者在类运行时将需要的class加载到JVM中。
2.执行引擎:负责执行class文件中包含的字节码指令
3.内存区(也叫运行时数据区)是在JVM运行的时候操作所分配的内存区。
运行时内存区主要可以划分为6个区域:
PC寄存器 :用于存储每个线程下一步将执行的JVM指令。
栈 :栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放当前线程中的局部基本类型的变量。
堆 :是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。堆是JVM所有线程共享的。
方法区 :存放了所加载的类信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息;通过class对象中的getName等方法来获取信息时,实际这些数据是来源于方法区,方法区是全局共享的。
运行时常量池 :存放类中固定的常量信息、方法和Field的引用信息等,其空间是从方法区中分配。
本地方法栈 :JVM采用本地方法栈来支持native方法的执行,此区域用于存储每个native方法调用的状
态。
加载顺序
方法区(Method Area) :用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分, 但它却有个别名non-heap(非堆)。方法区还包含一个运行时常量池。
java堆(Heap) :存储java实例或者对象的地方。这块是GC的主要区域(GC具体相关请查看之前的文章)。从存储的内容可以很容易知道,方法区和堆是被所有java线程共享的 。
java栈(Stack) :java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作
栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是线程私有的 。
程序计数器(PC Register) :用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的 。
本地方法栈(Native Method Stack) :和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
本地方法接口 :主要是调用C或C++实现的本地方法及返回结果。
扩展一个小知识:
如何判断对象是否存活
引用计数法 :给对象中添加一个引用计数器,当一个地方引用了对象,计数加1;当引用失效,计数器减
1;当计数器为0表示该对象已死、可回收;但很难解决循环引用问题;
可达性分析 :通过一系列称为"GC Root"的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径
称为引用链 ,当一个对象GC Roots没有任何引用链相连,则证明此对象已死、可回收。
Java中可以作为GC Roots的对象包括:
虚拟机栈中引用的对象、本地方法栈中native方法引用的对象、方法区静态属性引用的对象、方法区常量引用的对象。
总结
线程共享:方法区、堆
线程私有:java栈、本地方法栈、程序计数器
①堆空间(Heap):
存放创建出来的对象、对象中的属性,是共享的。
是Java虚拟机所管理的内存中最大的一块,在虚拟机启动时创建。线程共享,此内存区域的唯一目的就是存放
对象实例。
②方法区(Method Area):
存放方法、常量、静态变量、构造函数、字符串常量池,是共享的。
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
③栈区(JVM Stacks):
存放局部变量、方法的调用过程(栈帧)、方法的返回值,栈中的数据会随着栈的使用结束而销毁,是线程私有,生命周期与线程相同。
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)
用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
④程序计数器(Program Counter Register):
多线程切换执行时,保证每个线程都能正常切换。
线程私有,是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
⑤本地方法区(Native Method Stacks):
线程私有,与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
注: Native Method就是一个java调用非java代码的接口。