快速了解JVM虚拟机
1. JVM虚拟机介绍
什么是JVM虚拟机
JVM(Java Virtual Machine)是一种虚拟机,它是Java语言的核心和关键部分,作为Java平台的核心组件,主要的作用是在不同的操作系统和硬件平台上提供一个标准的、运行Java程序的环境。JVM执行Java程序时,将字节码转化为机器码执行,这样就可以保证Java程序能够在不同的操作系统上运行而不需要做任何修改。
优势特点
跨平台性:Java源代码一经编译就变成了字节码,这些字节码可以被JVM解释执行,因此Java程序可以在任何支持JVM的系统上运行,从而实现跨平台的特性。
内存管理:JVM可以为应用程序提供自动内存管理,这意味着Java程序员不需要手动分配和释放内存,JVM会负责进行垃圾回收和内存分配,从而提高了程序的稳定性和可靠性。
异常处理:JVM提供了强大的异常处理机制,Java程序员可以捕获和处理各种不同的异常,从而保证程序平稳运行。
安全性:JVM可以通过安全管理器(Security Manager)来控制Java应用程序的访问权限,从而提高了程序的安全性和可信度
高性能:JVM对字节码进行解释执行,可以通过Just-In-Time(JIT)编译技术将热点代码编译成本地机器码,从而提高程序的执行效率。
可扩展性:JVM提供了一些扩展API和机制,可以方便地扩展虚拟机的功能,例如Java Native Interface(JNI)、Java Management Extension(JMX)等。
易开发:Java编程语言在设计时考虑了程序员的使用体验,因此Java程序员可以采用面向对象编程的方法进行开发,从而提高程序的可读性、可维护性和可重用性。
2.JVM 执行过程
1.Java源代码通过Java编译器编译成字节码。
2.JVM类加载器将字节码加载到内存中,验证它是否满足安全要求并遵循正确的语法。
3.字节码存储在堆内存的方法区中。
4.字节码验证器确保字节码是类型安全的,并且不会访问未授权的内存。
5.解释器读取字节码指令并执行它们。另外,字节码也可以由即时编译器(JIT)编译成本地机器代码以获得更好的性能。
6.执行引擎使用基于栈的评估模型来执行操作。本地变量存储在栈上的槽中。
7.当遇到方法调用时,会将一个新的帧推入栈中,以容纳新方法的参数和本地变量。
8.当方法返回时,该帧将从栈中弹出并且返回值(如果有)被放置在上一个方法的栈帧顶部。
9.垃圾回收周期性执行,以回收不再使用的内存。
10.当抛出异常时,JVM的异常处理机制被触发。异常由相应的异常处理程序处理,如果找不到处理程序,则会将堆栈展开,直到找到适当的处理程序。如果找不到适当的处理程序,则JVM会打印堆栈跟踪并终止程序。
3. JVM 主要组成部分
类加载器、运行时数据区域、执行引擎
类加载器
类加载器负责加载Java类文件,并将其转换为JVM可以理解的二进制格式。JVM使用三个不同的类加载器,分别是启动类加载器、扩展类加载器和应用程序类加载器。启动类加载器加载JVM基础类,扩展类加载器加载JAVA_HOME目录下的类,应用程序类加载器从CLASSPATH环境变量中加载类。
类加载器有自己的命名空间,这允许它们严格控制哪些类可以被加载。当一个类被加载时,类加载器会将其存储在一块内存区域中,该内存区域称为方法区。
运行时数据区
运行时数据区是JVM中用于存储数据的区域。Java程序在实例化对象、调用方法以及执行其他操作时,运行时数据区才被使用。运行时数据区分为:
- 堆:用于存储Java对象,所有的对象都在堆中分配空间。
- 方法区:用于存储类信息、常量、静态变量等。
- JVM栈:用于存储Java方法的局部变量和操作数栈。
- 本地方法栈:用于支持本地方法的执行。
执行引擎
执行引擎使用指针引用方法区的常量池、类信息、方法信息、静态变量、成员变量等。执行引擎的主要作用是按照字节码指令将程序指令转换为实际操作,然后在运行时执行这些操作。
执行引擎将执行操作分成两类,一类是根据操作数栈进行操作,另一类是通过访问局部变量表进行操作。执行引擎执行的指令包括算术操作、类型转换、判断条件语句、方法调用等等。
为了提高执行效率,执行引擎还提供了即时编译器和解释器两种执行方式。当JVM检测到某段代码被执行频率较高时,就会通过即时编译器将其编译成本地机器指令,从而提高执行效率。
总之,JVM的类加载器、运行时数据区和执行引擎是JVM的核心组成部分。它们共同合作,使Java程序得以在跨平台的环境中运行,并提供高效的执行性能和内存管理机制。
4.深入了解类加载器
类加载器负责将编译好的Java代码(.class文件)加载到内存中,并形成对这些类的引用。Java的类加载器是一个负责查找和加载类文件的机制,它将Java程序中用到的类加载到JVM的内存中。
Java的类加载器主要有以下几种:
- Bootstrap ClassLoader:负责加载Java的核心类库(如Java API中的基本类)。
- Extension ClassLoader:负责加载Java扩展库和一些第三方类库,比如javax.*。
- System ClassLoader:负责加载应用程序的类库。
在JVM启动后,Bootstrap ClassLoader首先被启动,随后会加载Extension ClassLoader和System ClassLoader,它们形成了一条ClassLoader的层次关系链。当一个类需要被加载时,JVM按照这条ClassLoader层次关系链的顺序依次尝试加载对应的类,直到类被找到或者所有的ClassLoader尝试完毕。
类加载的过程
类加载的过程可以分为三个步骤:
- 加载:查找并加载类的二进制数据。
- 链接:将类的二进制数据合并到JVM的运行时环境中,并进行校验、解析等操作。
- 初始化:对类进行初始化,执行类的方法,对静态变量赋初值等。
在类加载的整个过程中,类加载器起到了重要作用,它负责加载和连接类的二进制数据,为JVM的运行时环境提供必要的支持。
类加载的器的特性
- 双亲委派机制:在类加载的过程中,ClassLoader按照一定的顺序尝试加载类,这个顺序是按照ClassLoader的层次关系链从上往下依次尝试。这个机制称为双亲委派机制,它的目的是为了保证类的唯一性和安全性。
- 热部署:通过动态生成.class文件,可以实现热部署的功能,即在应用程序运行期间可以动态地加载和卸载类。
- 自定义类加载器:在一些场景下,可能需要使用自定义的类加载器来加载特定的类或者资源,比如在Spring框架中就使用了自定义的类加载器。
- OSGi机制:OSGi是一种动态模块化的Java架构,通过类加载器和模块化机制可以实现高度可插拔的系统架构
5.详细了解运行时数据区
- 程序计数器(Program Counter Register):线程独有,记录当前线程执行到哪一行字节码指令,以便在发生分支、循环、子函数等转移时能恢复执行位置。
- Java虚拟机栈(Java Virtual Machine Stacks):线程独有,用于保存Java方法执行的线程内部局部变量、操作数栈、动态链接、方法出口等信息。每个方法执行时,Java虚拟机都会创建一个栈帧用于保存这些信息,方法执行后,相应的栈帧也会随之销毁。
- 本地方法栈(Native Method Stack):与Java虚拟机栈类似,但是为Java虚拟机执行本地方法服务。
- Java堆(Java Heap):用于存储Java类实例化对象和数组等动态分配的内存空间。Java堆是Java虚拟机管理的最大的一块内存区域。
- 方法区(Method Area):存储被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据。也被称为永久代(Permanent Generation),但在JDK 8之后,永久代被移除,改为使用元空间(Metaspace)来代替。
- 运行时常量池(Runtime Constant Pool):方法区的一部分,用于存储在编译期确定的一些常量,如字符串常量、final变量等。运行时常量池是每个类和接口的构成部分之一,在JVM加载类或接口时创建。
6.了解执行引擎
执行引擎是JVM的核心组件之一,它通过解释执行Java字节码来实现跨平台的特性。JVM执行引擎主要包括解释器和即时编译器(JIT),其中解释器可以直接解释Java字节码并执行,而JIT会将Java字节码编译成本地机器码后再执行,从而提高执行效率。
解释器执行过程:
- 读入字节码:解释器会读入Java字节码,并将其解析成指令流。
- 解释执行:解释器会逐条解释指令并执行,将结果保存在操作数栈、局部变量表等数据结构中。
- 栈帧切换:每个线程在执行Java方法时都会有一个对应的栈帧,当进入一个方法时,解释器会创建一个新的栈帧,并将其压入虚拟机栈中;当方法执行完毕后,解释器会将该栈帧弹出,栈帧切换到上一个方法继续执行。
JIT编译器执行过程:
- 读入字节码:JIT编译器会读入Java字节码,并将其解析成指令流。
- 分析优化:JIT编译器会对指令流进行静态分析,并根据分析结果进行一系列优化,包括方法内联、常量折叠、去除死代码等。
- 编译执行:JIT编译器将优化后的Java字节码编译成本地机器码后执行,从而提高执行效率。编译后的机器码也可以缓存,下次执行同样的代码时直接执行编译后的机器码。
执行器总结:
JVM执行引擎是通过解释执行Java字码并执行,才能实现跨平台的特性。具体来说,当Java程序编译成字节码后,JVM执行引擎就会读入这些字节码,并根据其指令集进行解释或编译执行。JVM执行引擎是JVM的核心组件之一,其中包含解释器和即时编译器(JIT),它们分别使用不同的方式来执行Java字节码。
解释器通过逐条解释并执行Java字节码来实现程序的运行,而即时编译器则会将Java字节码编译成本地机器码,然后再执行。相比于解释执行,编译执行具有更高的性能和更好的优化效果。但是,由于编译需要时间,所以在程序刚启动时,解释器通常会比即时编译器更快。
JVM执行引擎的另一个重要组成部分是垃圾收集器(GC),它用于管理Java程序中的内存分配和回收。当Java程序创建了一个对象时,垃圾收集器会自动跟踪这个对象的使用情况,并在对象不再被使用时将其回收。垃圾收集器可以帮助程序员避免手动管理内存分配和回收,从而减少程序中的错误和漏洞。
7.学习垃圾回收机制
在执行Java程序时,JVM会在堆中分配内存用于存储对象,当对象不再被引用时就会成为垃圾,这些垃圾对象需要被清除,JVM的垃圾回收机制就是用来清除这些垃圾对象的。
垃圾回收算法:
- 标记-清除算法:首先标记所有还在使用的对象,然后清除未标记的对象,但是这种算法会产生内存碎片。
- 标记-整理算法:首先标记所有还在使用的对象,然后将这些对象移到内存的一端,然后直接清除较大块的未标记内存。
- 复制算法:将垃圾回收时还存活的对象复制到一块新的内存,然后清除原来的内存。
- 分代回收算法:将堆空间划分为新生代和老年代,新生代存放新生的对象,老年代存放存活时间较长的对象,针对不同年龄段的对象选择不同的回收算法。
其中第四种算法是目前主要的垃圾回收算法,JVM中使用的垃圾回收器包括Serial、CMS、G1等等。不同的垃圾回收器实现了不同的垃圾回收算法,根据应用程序的不同,可以选择合适的垃圾回收器,以达到更好的性能和更少的停顿时间。
常用垃圾回收器:
1. Serial垃圾回收器
Serial是JVM中最基本的垃圾回收器之一,用于新生代的垃圾回收。它采用“复制算法”(Copying Algorithm),将新生代分为一个较大的Eden空间和两个较小的Survivor空间,将对象首先分配在Eden空间中,然后进行垃圾回收时,会将Eden空间中的存活对象移动到其中一个Survivor区中,同时清理Eden空间和另一个Survivor区中的垃圾对象。每次回收都会在两个Survivor区中选择一个空的区域来进行存活对象的保留。该算法简单高效,但不能处理大型和长寿命的对象,并且会产生内存碎片。
2. CMS垃圾回收器
CMS是一种用于老年代的垃圾回收器,它采用“标记-清除算法”(Mark-Sweep Algorithm),并且支持并发回收,从而减少了垃圾回收时的停顿时间。首先,它会在堆内存中标记所有存活的对象,然后会清除所有未标记对象的内存,最后,它会执行一次迭代,来确保所有对象都已被正确的标记和清除。CMS在垃圾回收时,会产生浮动垃圾(Floating Garbage),需要协调处理。
3. G1垃圾回收器
G1(Garbage First)是一种用于高吞吐量(High Throughput)应用的垃圾收集器,它将整个Java堆划分为了一个大小相等的几个Region,并尽量避免产生内存碎片。它采用“分代垃圾回收算法”(Generational Garbage Collection Algorithm),将堆内存划分为新生代和老年代。G1的工作模式分为以下几个步骤:
- 初始标记阶段(Initial Marking Phase):标记与根对象直接关联的可达对象。
- 并发标记阶段(Concurrent Marking Phase):执行全局标记,标记剩余的可达对象。
- 最终标记阶段(Final Marking Phase):标记产生在并发标记期间的新添加的对象。
- 筛选回收阶段(Live Data Counting and Evacuation Phase):从最具价值的Region开始进行垃圾回收。
G1的垃圾回收操作与应用程序的执行是分散进行的,因此,它具有更平均的响应性和更少的停顿时间。
8.了解常用的JVM性能分析工具
JVM性能分析工具可以帮助开发人员进行必要的性能分析,以找出程序性能瓶颈和优化的机会。其中,JConsole和JVisualVM是JVM中常用的性能分析工具。
1. JConsole
JConsole是JDK自带的一个Java监控工具,用于监控和管理JVM,它对JVM的各种资源(如内存、线程、类、CPU使用情况等)进行可视化展示。JConsole的主要功能包括:监控JVM的资源使用情况、MBean的管理和操作、执行JMX代理方法等。JConsole可以用于在开发环境中快速定位性能问题,也可以用于在生产环境中对JVM进行监控。
2. JVisualVM
JVisualVM是一种多合一的性能分析工具,提供了诸如CPU以及堆分析工具等,是一个很好的Java虚拟机性能分析工具。它可以监测应用程序的线程、内存、CPU以及类的加载和卸载情况等,并且还支持创建内存、CPU和线程转储文件进行离线分析。在JVisualVM中,开发人员可以使用不同的插件来扩展它的功能。
3. JProfiler
JProfiler是功能丰富的Java应用程序性能分析工具,主要用于性能优化、探测内存泄漏及线程问题等。它支持CPU分析、内存分析、线程分析等多种分析方式,并提供了一些性能优化建议,帮助开发人员快速提高程序性能。
4.Flight Recorder
Flight Recorder是JDK自带的事件记录器,可以记录Java应用程序在运行时的各种事件信息,如CPU、垃圾回收等。它可以提供详细的性能统计信息,帮助开发人员分析程序的性能问题,并且可以导出数据,供离线分析使用
5. Java Mission Control
Java Mission Control是Oracle提供的一款强大的JVM性能分析工具,它可以帮助开发人员深入分析JVM的性能问题,包括性能瓶颈、内存泄漏等。Java Mission Control可以监测并分析应用程序在运行时的各种指标,如CPU、内存、线程、垃圾回收等。它提供了详细的分析报告和可视化图表,方便开发人员直观的了解程序的性能。
6. VisualGC
VisualGC是一个开源的JVM性能分析工具,它可以用于监控JVM的内存、垃圾回收、类和线程等信息。VisualGC提供了可视化的图表,帮助开发人员更直观地了解JVM的性能和资源使用情况。它是一个轻量级的工具,易于安装和使用。
7. Java Heap Profiler
Java Heap Profiler是一款强大的JVM性能分析工具,它可以帮助开发人员深入分析和优化Java应用程序的性能。它可以监控JVM的内存使用情况,并快速定位内存泄漏问题。Java Heap Profiler提供了多种分析方式,包括可视化图表、树状图等,方便开发人员查看JVM内存使用情况。
结尾
欢迎加群交流一起讨论计算机相关知识:QQ群259352297 不卖课
欢迎来观看我做的一些教程视频Setruth的个人空间_哔哩哔哩_bilibili