Java虚拟机(JVM)是Java程序运行的核心环境,它负责将Java字节码转换为机器码并执行。本文将深入解析JVM的运行时数据区、类加载机制以及执行引擎,帮助读者更好地理解JVM的工作原理。
一、运行时数据区(Runtime Data Area)
运行时数据区是JVM在执行Java程序时分配的内存区域,主要包括以下几个部分:
1. 方法区(Method Area)
方法区用于存储类信息、常量、静态变量以及即时编译器编译后的代码。它是线程共享的区域,所有线程共享方法区内存。
-
JDK 8之前:HotSpot使用永久代(PermGen)来实现方法区。
-
JDK 8及以后:永久代被移除,改为使用元空间(Metaspace),元空间使用本地内存(Native Memory)来提高性能和避免OOM错误。
2. 堆(Heap)
堆是JVM中最大的内存区域,用于存储所有线程共享的对象和数组,是垃圾回收的主要区域。
-
新生代(Eden):新对象最初会被分配到Eden区,Eden区较大,频繁进行垃圾回收。
-
幸存区(Survivor):两个Survivor区S0和S1交替使用,新对象在经历一次垃圾回收后会存放到其中一个Survivor区,进一步存活的对象会移动到另一个Survivor区,最终晋升到老年代。
-
老年代:长生命周期对象经过多次垃圾回收后会被迁移到老年代,Major GC在老年代进行,频率较低但耗时长。
3. 虚拟机栈(JVM Stack)
虚拟机栈是线程私有的,每个线程创建时都会创建一个虚拟机栈。虚拟机栈用于存储局部变量表、操作栈、方法出口等信息。
-
栈帧(Stack Frame):每当一个方法被调用时,虚拟机会在栈中创建一个新的栈帧,该栈帧用于存储方法的局部变量表、操作栈、方法出口等信息。方法执行完后,栈帧会被弹出,释放内存。
4. 本地方法栈(Native Method Stack)
本地方法栈也是线程私有的,用于为本地方法服务。它使用JNI(Java Native Interface)调用的本地代码在此区域分配内存。
5. 程序计数器(Program Counter Register)
程序计数器是线程私有的,每个线程都有一个独立的程序计数器。它用于记录当前字节码行号,是唯一不会出现OOM的内存区域。
二、类加载机制(Class Loading)
类加载机制是JVM将类信息加载到内存中的过程,主要包括以下几个阶段:
1. 加载(Loading)
加载阶段将类的二进制数据读入内存,并创建一个Class
对象。
-
启动类加载器(Bootstrap ClassLoader):主要负责加载
<JAVA_HOME>\lib
目录或被-Xbootclasspath
指定的路径中的类库。 -
拓展类加载器(Extension ClassLoader):主要负责加载
<JAVA_HOME>\lib\ext
目录下的类库。 -
应用类加载器(Application ClassLoader):主要负责加载用户类路径(classpath)上的类库。
2. 链接(Linking)
链接阶段包括验证、准备和解析三个步骤。
-
验证(Verification):验证加载的类信息是否符合JVM规范。
-
准备(Preparation):为类的静态变量分配内存,并设置初始值。
-
解析(Resolution):将符号引用转换为直接引用。
3. 初始化(Initialization)
初始化阶段执行类的静态初始化块,为静态变量赋初值。
三、执行引擎(Execution Engine)
执行引擎负责将字节码转换为机器指令并执行,主要包括以下两个部分:
1. 解释器(Interpreter)
解释器逐行解释字节码并执行,适用于程序首次运行时。
2. 即时编译器(JIT Compiler)
即时编译器将热点代码(频繁执行的代码)编译为机器码执行,以提升效率。JIT还可能会发生指令重排序,一般用volatile
关键字解决该问题。
编译类型
-
C1编译器(Client Compiler):用于快速启动的轻量级优化,适用于客户端应用程序。
-
C2编译器(Server Compiler):用于长时间运行的重度优化,适用于服务器端应用程序。
四、本地方法接口(JNI)
本地方法接口(JNI)允许Java调用非Java代码(如C/C++),便于与操作系统或其他本地库进行交互。JNI提供跨语言调用能力,使Java程序可以访问操作系统级别的功能或高性能库。
标签:Java,虚拟机,线程,内存,JVM,加载 From: https://blog.csdn.net/m0_73355421/article/details/145225732