目录
一、JVM是什么?
JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。
出处:JVM是什么?深入解析JVM原理! - 掘金 (juejin.cn)https://juejin.cn/post/6844903881063792647
二、JVM有哪些部分组成
组成:运行时数据区域、类加载器、执行引擎。
运行时数据区域:
- 程序计数器(Program Counter Register):存储当前线程执行的字节码指令地址。
- Java堆(Java Heap):存储Java对象实例,是Java程序运行时的动态内存区域。
- 方法区(Method Area)(在JDK1.8之后被元空间所取代):存储类的元数据信息、静态变量、常量等数据,也称为永久代(Permanent Generation)。
- 运行时常量池(Runtime Constant Pool):每个类或接口在编译时生成一个对应的常量池表,在运行时被加载到方法区的运行时常量池中。
- 虚拟机栈(Java Virtual Machine Stacks):每个Java方法在执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 本地方法栈(Native Method Stack):为Java虚拟机使用到的Native方法服务,与Java虚拟机栈类似,但是用于执行本地方法。
- 直接内存(Direct Memory):通过NIO库可以直接使用内存,而不受Java堆的限制,也属于JVM的一部分。
如下图:https://javaguide.cn/java/jvm/memory-area.htm
运行时数据区域中,可以分为线程共享,和线程私有;
线程共享包含:堆、方法区、直接内存 (非运行时数据区的一部分)、运行时常量池。线程共享区域提供了多个线程之间共享数据的机制,从而实现了多线程编程的基础。
线程私有包含:虚拟机栈,本地方法区,程序计数器。线程私有区域保证了每个线程在执行过程中都有自己独立的内存空间,互不干扰。这些区域会随着线程的产生而产生,消亡而消亡。
1、程序计数器
可以将程序计数器看做是一个指针,它指向线程正在运行时的字节码,每一个线程都有一个自己的程序计数器,在进行线程切换时,以便线程找到上次运行停歇的位置继续运行。
2、Java堆
存储Java对象实例,是Java程序运行时的动态内存区域。几乎所有的对象实例和数组都存在于堆中。Java堆也是垃圾收集器管理的主要区域,因此也被称为GC堆。
堆通常分为三个区域:新生代,老年代,永久代(后被元空间取代,存在于本地内存)。
新生代分为三个部分:Eden,s0,s1等。这些分区都是为更好的进行垃圾回收。
3、虚拟机栈
虚拟机栈是每个线程所私有的。栈是由栈帧组成,而一个栈帧是由局部变量表、操作数栈、动态链接、方法返回地址组成。
局部变量表(Local Variable Table):用于存储方法执行过程中的局部变量。在方法被调用时,局部变量表会被创建,用于存储方法参数以及方法内部定义的局部变量。局部变量表中的槽位可以存储各种数据类型的值,包括基本类型和对象引用等。
操作数栈(Operand Stack):也称为操作栈,用于执行方法时进行数据操作的地方。操作数栈是一个后进先出(LIFO)的栈结构,在方法执行过程中,计算过程中的临时数据和结果会被存储在操作数栈中,供字节码指令进行操作。
动态链接(Dynamic Linking):在 Java 虚拟机中,动态链接是指在运行时解析常量池中的符号引用,将其转换为直接引用的过程。动态链接的主要目的是为了支持方法调用、字段访问等操作。
方法返回地址(Return Address):用于记录方法调用完成后返回到调用者的地址。在方法被调用时,返回地址会被存储在方法栈的栈帧中,当方法执行完成后,虚拟机会根据返回地址返回到调用点继续执行。
如图:https://javaguide.cn/java/jvm/memory-area.htm
虚拟机栈是jvm的一个核心。几乎所有的方法的调用都是通过栈来实现的。方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。
4、方法区(元空间)
主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。在一些情况下也会进行垃圾回收。
5、运行时常量池(元空间中分配)
顾名思义,是用来存放运行时的常量的。在方法区中分配空间。整数、浮点数和字符串字面量。常见的符号引用包括类符号引用、字段符号引用、方法符号引用、接口方法符号。
6、字符串常量池
字符串常量池是 Java 中用于存储字符串字面量的一个特殊区域,它位于方法区(在 Java 8 及之前的版本)或元空间(在 Java 8 及之后的版本)中。是为了减小内存损耗的。
7、本地方法栈
为Java虚拟机使用到的Native方法服务,与Java虚拟机栈类似,但是用于执行本地方法。
Native方法是什么?
Native方法是指使用本地语言(如C或C++)编写的方法,而不是Java语言编写的方法。在Java程序中,可以通过native关键字声明一个方法为本地方法,然后在本地语言中实现这个方法的具体功能。
当Java程序调用一个native方法时,实际上是调用了本地语言中对应的函数。这样的设计使得Java程序可以与底层的操作系统或其他本地资源进行交互,例如调用操作系统提供的特定功能或调用底层硬件的接口。
8、直接内存
直接内存并不是运行时数据区域中的。它是 Java 中一种特殊的内存分配方式,与 Java 虚拟机堆内存不同,它不受 Java 堆大小限制,也不会受到垃圾回收的管理。直接内存通常是通过 NIO(New I/O)包中的 ByteBuffer 类来操作的。
具体来说,直接内存是通过使用操作系统的本地内存来分配和管理的,而不是通过 Java 虚拟机来分配。这样做的主要目的是为了提高 I/O 操作的性能,因为直接内存可以绕过 Java 堆,直接与操作系统内核进行交互,减少了数据在 JVM 堆和本地堆之间的拷贝过程。
类加载器:
类加载器(Class Loader)是 Java 虚拟机(JVM)的一个重要组成部分,负责加载 Java 类文件到内存中,并生成对应的 Class 对象。类加载器是 Java 实现动态性和灵活性的关键之一,它可以根据需要从不同的来源加载类文件,如本地文件系统、网络、数据库等。
1、类加载过程
①加载(Loading): 加载阶段是指将类的字节码文件加载到内存中,并生成对应的 Class 对象。类加载器通过指定的类全限定名(fully qualified class name)或类文件的路径来获取类文件的字节码数据。
②链接(Linking): 链接阶段分为三个步骤:验证、准备和解析。
验证(Verification):确保加载的类符合 JVM 规范,不会危害虚拟机的正确运行。
准备(Preparation):为类变量(静态变量)分配内存空间,并初始化为默认值(零值)。
解析(Resolution):将类、接口、字段和方法的符号引用转换为直接引用。
③初始化(Initialization): 初始化阶段是类加载的最后一步,它负责执行类构造器(<clinit> 方法)的过程。在这个阶段,JVM 会按照程序代码的顺序执行类的静态变量赋值和静态代码块的代码,并确保类的初始化是线程安全的。
Class对象?反射机制?
在Java中,Class对象是用来表示已加载类或接口的元数据,它包含了有关类或接口的结构信息,比如类的字段、方法、构造函数等。每个类在内存中都有一个对应的Class对象,该对象在类被加载到内存中时由JVM自动生成。
Class对象可以通过以下方式获取:
- 调用对象的getClass()方法:例如,对于一个已实例化的对象obj,可以通过obj.getClass()获取其对应的Class对象。
- 使用.class字面量:例如,对于类Foo,可以使用Foo.class来获取其对应的Class对象。
- 使用Class类的静态方法:例如,可以使用Class.forName("类的全限定名")方法来获取指定类的Class对象。
通过Class对象,可以进行诸如实例化对象、获取类的结构信息、调用类的方法等操作,从而实现Java的反射机制。Class对象是Java语言的一种重要特性,它使得程序在运行时可以动态地加载、操作和使用类,从而提高了程序的灵活性和可扩展性。
详情可参考:https://javaguide.cn/java/jvm/classloader.htm 非常详细!
执行引擎:
执行引擎是Java虚拟机的一部分,负责执行已加载的字节码指令。它解释字节码指令并执行对应的操作,从而实现Java程序的运行。执行引擎通常包括以下几个组成部分:
-
解释器(Interpreter): 解释器逐条解释字节码指令,并将其翻译成底层操作系统或硬件的指令,然后执行。
-
即时编译器(Just-In-Time Compiler,JIT): 即时编译器将字节码直接编译成本地机器码,以提高执行效率。JIT编译器根据程序的运行情况来动态地选择需要编译的代码块,并将其优化成更高效的形式。
-
垃圾回收器(Garbage Collector): 垃圾回收器负责在运行时自动回收不再使用的内存,以避免内存泄漏和程序运行过程中的内存溢出错误。
-
本地方法接口(Native Method Interface,JNI): 执行引擎通过本地方法接口与底层操作系统或其他编程语言交互,调用本地方法实现对底层资源的访问,如文件系统、网络等。
执行引擎是Java程序执行的核心组件之一,它负责将字节码指令转换为具体的操作,并实现程序的功能逻辑。