1、什么是JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。
Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。
2、JDK、JRE和JVM关系
![](https://img-blog.csdnimg.cn/20210609163620837.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDE2MTkx,size_16,color_FFFFFF,t_70)
JDK = JRE + 开发工具(如编译器javac.exe)
JRE = JVM + 类库
![](https://img-blog.csdnimg.cn/20210609171906795.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDE2MTkx,size_16,color_FFFFFF,t_70)
Java程序的开发过程为:
1、我们利用 JDK (调用 Java API)编写出 Java 源代码,存储于 .java 文件中
2、JDK 中的编译器 javac 将 Java 源代码编译成 Java 字节码,存储于 .class 文件中
3、JRE 加载、验证、执行 Java 字节码
4、JVM 将字节码解析为机器码并映射到 CPU 指令集或 OS 的系统调用。
3、JVM的组成结构
![](https://img-blog.csdnimg.cn/2021060917191643.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDE2MTkx,size_16,color_FFFFFF,t_70)
JVM包含两个子系统和两个组件
两个子系统:
- Class loader(类装载)
- Execution engine(执行引擎)
两个组件:
- Runtime data area(运行时数据区)
- Native Interface(本地接口)
![](https://img-blog.csdnimg.cn/20210608181307331.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0NDE2MTkx,size_16,color_FFFFFF,t_70)
1、程序计数器(PC Register)
作用:记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空。
异常:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2、虚拟机栈(JVM Stacks)
作用:是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧用于存储数据。每一个方法从调用直至执行完成,对应栈帧在虚拟机栈中入栈到出栈的过程。
异常:会抛出StackOverflowError和OutOfMemoryErrror异常。
栈帧(Stack Frame)包含以下几个部分
1、局部变量表:是存放方法参数和局部变量的区域
2、操作栈:是个初始状态为空的桶式结构栈。在方法执行过程中,会有各种指令往栈中写入和提取信息。JVM的执行引擎式基于栈的执行引擎,其中的栈指的就是操作栈。i++和++i(字节码指令)
3、动态链接:每个栈帧中包含一个在常量池中对当前方法的引用,目的是支持方法调用过程的动态连接
4、方法返回地址:方法执行有两种退出情况,正常退出和异常退出,两者都将返回至方法当前被调用的位置。方法退出的过程相当于弹出当前栈帧
3、本地方法栈(Native Method Stacks)
作用:与虚拟机栈所发挥的作用非常相似,为虚拟机使用到的Native方法服务。线程开始调用本地方法时,会进入不再受JVM约束的世界。本地方法可以通过 JNI(Java Native Interface)来访问虚拟机运行时的数据区。
异常:会抛出StackOverFlowError和OutOfMemoryError异常。
4、方法区(Method Area)
作用:用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
异常:当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
垃圾回收:垃圾收集行为在这个区域时比较少出现的,其内存回收目标主要是对常量池的回收和对类型的卸载。JDK8之前,Hotspot中方法区的实现是永久代,JDK8开始使用元空间(Meta Space),以前永久代所有内容的字符串常量移至元空间,元空间直接在本地内存分配。
运行时常量池
1、是方法区的一部分
2、存放编译期生成的各种字面量和符号引用
3、Class文件中除了存有类的版本、字段、方法、接口等描述信息,还有一项是常量池,存有这个类的编译期生成的各种字面量和符合引用,这部分内容将在类加载后,存放到方法区的运行时常量池中
为什么废除1.7中永久代?
1、移除永久代是为融合Hotspot JVM与JR'ockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代
2、现实使用中,由于永久代内存经常不够用或发生内存泄露,爆出异常 java.lang.OutOfMemoryError: PermGen
3、基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间
5、堆(Heap)
作用:Java虚拟机所管理的内存中最大的一块。Java堆是被线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
异常:如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
6、直接内存(Direct Memory)
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。
在 JDK 1.4 中新加入了 NIO,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
程序计数器、虚拟机栈、本地方法栈是线程私有的,方法区、堆是线程共享的
5、方法运行流程