最近在看JVM,想知道在JVM执行引擎把字节码编译成机器码后,cup执行该机器码指令时,根据指令需求加载所需类或者载入数据到堆或虚拟机栈中吗?
在JVM执行字节码时,字节码会被解释器逐条解释执行,也可以通过即时编译器(JIT)将字节码编译成本地机器码,然后直接在CPU上执行。在执行机器码时,CPU会根据指令需求加载所需的类或者载入数据到堆或虚拟机栈中。这些操作都是由CPU硬件实现的,与JVM执行引擎无关。JVM执行引擎只负责将字节码转换成机器码,并将机器码交给CPU执行。
答案:
当JVM执行引擎将字节码编译成机器码后,CPU执行这些指令时,并不会在需要时立即加载所需类或将数据载入堆或者虚拟机栈中,而是在需要时才执行这些操作。具体的加载时机和运行时数据区的载入时机如下:
1.类的加载时机:
在程序运行期间,JVM会分别对类或接口进行加载,连接和初始化。Java虚拟机规范把这个过程称为类的生命周期(Class Lifecycle)。在类被加载到Java虚拟机中之前,Java虚拟机必须确定这个类的全名。Java虚拟机根据类的全名来加载指定的类。在类被加载的过程中,Java虚拟机会执行以下操作:
1.加载:在这一阶段,Java虚拟机会查找并加载指定的类的二进制数据。这个查找过程一般有类装载器完成。
2.链接:在这一阶段,Java虚拟机会验证类中的信息,为静态域分配空间并且赋予初始值,解析对其他类或接口的引用。
3.初始化:在这一阶段,Java虚拟机会执行类的初始化工作。这包括执行类构造器()方法的语句,赋值给静态域等。
类的加载分为三个阶段:加载阶段、连接阶段和初始化阶段。其中,加载阶段可以进一步细分为类路径搜索和加载二进制字节流两个步骤。
2.运行时数据区的载入时机:
Java虚拟机运行时数据区包括堆、虚拟机栈、本地方法栈、程序计数器和方法区。这些数据区在Java虚拟机启动时就已经创建好,但其中部分区域的内存空间需要在运行时按需动态地分配。
堆:堆是Java虚拟机中最大的一块内存,也是被所有线程共享的。Java虚拟机规范规定,堆需要在JVM启动时预设一个初始大小,之后堆的大小可以自动扩展,直到达到最大限制。当堆中没有足够的内存空间来支持对象创建时,将会触发垃圾回收机制来回收不再使用的对象空间,以便为新创建的对象腾出空间。
虚拟机栈:虚拟机栈为线程私有,它的生命周期与线程相同,用于存储方法调用时的局部变量表、操作数栈、动态链接、方法出口等信息。每个方法在执行时,都会创建一个栈帧,栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。方法被调用时,会把栈帧压入相应线程的虚拟机栈中,方法调用结束时,相应的栈帧会从虚拟机栈中弹出。
本地方法栈:本地方法栈用于支持Native方法执行。本地方法栈的作用和虚拟机栈类似,但是本地方法栈的内存空间用于执行Native方法。
程序计数器:程序计数器是一块非常小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。Java虚拟机需要在任何时候都能够恢复到方法字节码流的某个位置继续执行,程序计数器就起到了这个作用。
方法区:方法区主要用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
以上是Java虚拟机运行时数据区的五个部分,其中只有堆和方法区是线程共享的。堆和方法区的内存空间需要在Java虚拟机启动时预设初始大小,之后堆的大小可以自动扩展至最大限制。而虚拟机栈、本地方法栈、程序计数器等部分的内存空间则是根据实际需要来动态分配的。
结合以上知识,可以讲述在程序运行期间,JVM会分别对类或接口进行加载、连接和初始化,而JVM执行引擎将字节码编译成机器码后,CPU执行这些指令时,并不会在需要时立即加载所需类或将数据载入堆或者虚拟机栈中。堆和方法区是线程共享的,内存空间需要在Java虚拟机启动时预设初始大小,之后堆的大小可以自动扩展至最大限制。
具体的排查 CPU 飙高