官方资料:
在线视频:尚硅谷JVM精讲与GC调优教程(宋红康主讲,含jvm面试真题)
课程资料:尚硅谷宋红康JVM精讲与GC调优
代码仓库:
GitHub: https://github.com/Shiguang-coding/learn-jvm
Gitee: https://gitee.com/an_shiguang/learn-jvm
1、JVM概述
1.1、Java语言及Java生态圈
Oracle JDK与Open JDK什么关系?

官方的说明:
https://www.oracle.com/cn/java/technologies/javase-downloads.html
Oracle Customers and ISVs targeting Oracle LTS releases: Oracle JDK is Oracle’s supported Java SE version for customers and for developing, testing, prototyping or demonstrating your Java applications.
End users and developers looking for free JDK versions: Oracle OpenJDK offers the same features and performance as Oracle JDK under the GPL license .
Oracle JDK下载路径:
https://www.oracle.com/java/technologies/javase-jdk15-downloads.html
Open JDK下载路径:
https://cn.azul.com/downloads/zulu-community/?version=java-11-lts&os=windows&architecture=x86-64-bit
Oracle与OpenJDK之间的主要区别
- Oracle JDK版本将每三年发布一次LTS版本,而OpenJDK版本每三个月发布一次。
- Oracle JDK将更多地关注稳定性,它重视更多的企业级用户,而OpenJDK经常发布以支持其他性能,这可能会导致不稳定。
- Oracle JDK支持长期发布的更改,而Open JDK仅支持计划和完成下一个发行版。
- Oracle JDK根据二进制代码许可协议获得许可,而OpenJDK根据GPL v2许可获得许可。 使用Oracle平台时会产生一些许可影响。如Oracle 宣布的那样,在没有商业许可的情况下,在2019年1月之后发布的Oracle Java SE 8的公开更新将无法用于商业,商业或生产用途。但是,OpenJDK是完全开源的,可以自由使用。
- Oracle JDK的构建过程基于OpenJDK,因此OpenJDK与Oracle JDK之间没有技术差异。
- 顶级公司正在使用Oracle JDK,例如Android Studio,Minecraft和IntelliJ IDEA开发工具,其中Open JDK不太受欢迎。
- Oracle JDK具有Flight Recorder,Java Mission Control和Application Class-Data Sharing功能,Open JDK具有Font Renderer功能,这是OpenJDK与Oracle JDK之间的显著差异。
- Oracle JDK具有良好的GC选项和更好的渲染器,而OpenJDK具有更少的GC选项,并且由于其包含自己的渲染器的分布,因此具有较慢的图形渲染器选项。
- 在响应性和JVM性能方面,Oracle JDK与OpenJDK相比提供了更好的性能。
- 与OpenJDK相比,Oracle JDK的开源社区较少,OpenJDK社区用户的表现优于Oracle JDK发布的功能,以提高性能。
- 如果使用Oracle JDK会产生许可影响,而OpenJDK没有这样的问题,并且可以以任何方式使用,以满足完全开源和免费使用。
- Oracle JDK在运行JDK时不会产生任何问题,而OpenJDK在为某些用户运行JDK时会产生一些问题。
- 根据使用方的使用和许可协议,现有应用程序可以从Oracle JDK迁移到Open JDK,反之亦然。
- Oracle JDK将从其10.0.X版本将收费,用户必须付费或必须依赖OpenJDK才能使用其免费版本。
- Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本。
- Oracle JDK以前的1.0版以前的版本是由Sun开发的,后来被Oracle收购并为其他版本维护,而OpenJDK最初只基于Java SDK或JDK版本7。
- Oracle JDK发布时大多数功能都是开源的,其中一些功能免于开源,并且根据Sun的许可授权,而OpenJDK发布了所有功能,如开源和免费。
- Oracle JDK完全由Oracle公司开发,而Open JDK项目由IBM,Apple,SAP AG,Redhat等顶级公司加入和合作。
JDK与JVM是什么关系?

如何理解Java是跨平台的语言?

“write once, run anywhere.”(一次编译,处处运行)
当Java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译。
这个优势不再那么吸引人了。Python、PHP、Perl、Ruby、Lisp等有强大的解释器。跨平台似乎已经快成为一门语言必选的特性。
如何理解JVM跨语言的平台?

Java虚拟机根本不关心运行在其内部的程序到底是使用何种编程语言编写的,它只关心“字节码”文件。
Java不是最强大的语言,但是JVM是最强大的虚拟机。
Java不存在内存溢出?内存泄漏?

java = (c++)–;
垃圾收集机制为我们打理了很多繁琐的工作,大大提高了开发的效率,但是,垃圾收集也不是万能的,懂得JVM内部的内存结构、工作机制,是设计高扩展性应用和诊断运行时问题的基础,也是Java工程师进阶的必备能力。
Java发展的几个重大事件?
-
2000年,JDK 1.3发布,Java HotSpot Virtual Machine正式发布,成为Java的默认虚拟机。
-
2002年,JDK 1.4发布,古老的Classic虚拟机退出历史舞台。
-
2003年年底,Java平台的Scala正式发布,同年Groovy也加入了 Java阵营。
-
2006年,JDK 6发布。同年,Java开源并建立了 OpenJDK。顺理成章,Hotspot虚拟机也成为了 OpenJDK中的默认虚拟机。
-
2007年,Java平台迎来了新伙伴Clojure。
-
2008 年,Oracle 收购了 BEA,得到了 JRockit 虚拟机。
-
2009年,Twitter宣布把后台大部分程序从Ruby迁移到Scala,这是Java平台的又一次大规模应用。
-
2010年,Oracle收购了Sun,**获得Java商标和最具价值的HotSpot虚拟机。**此时,Oracle拥有市场占用率最高的两款虚拟机HotSpot和JRockit,并计划在未来对它们进行整合:HotRockit. JCP组织管理:Java语言
-
2011年,JDK7发布。在JDK 1.7u4中,正式启用了新的垃圾回收器G1。
-
2017年,JDK9发布。将G1设置为默认GC,替代CMS (被标记为Deprecated)
同年,IBM的J9开源,形成了现在的Open J9社区 -
2018年,Android的Java侵权案判决,Google赔偿Oracle计88亿美元
同年,JDK11发布,LTS版本的JDK,发布革命性的ZGC,调整JDK授权许可
-
2019年,JDK12发布,加入RedHat领导开发的Shenandoah GC
说说你认识的JVM?
-
Sun Classic VM -->解释型
-
Exact VM --> Solaris
-
SUN公司的 HotSpot VM
-
BEA 的 JRockit --> 不包含解释器,服务器端,JMC
-
IBM 的 J9
-
KVM和CDC/CLDC Hotspot
-
Azul VM
-
Liquid VM
-
Apache Harmony
-
Microsoft JVM
-
TaobaoJVM
-
Graal VM --> 2018年,“Run Programs Faster Anywhere”

-
Dalvik VM
-
其他JVM:Java Card VM、Squawk VM、JavaInJava、Maxine VM、Jikes RVM、IKVM.NET、Jam VM、Cacao VM、Sable VM、Kaffe、Jelatine JVM、Nano VM、MRP、Moxie JVM
JVM的生命周期?
虚拟机的启动
Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的。
虚拟机的退出有如下的几种情况:
- 某线程调用Runtime类或System类的exit方法,或 Runtime类的halt方法,并且Java安全管理器也允许这次exit或halt操作。
- 程序正常执行结束
- 程序在执行过程中遇到了异常或错误而异常终止
- 由于操作系统出现错误而导致Java虚拟机进程终止
重点说下HotSpot?
- SUN的JDK版本从1.3.1开始运用HotSpot虚拟机, 2006年底开源,主要使用C++实现,JNI接口部分用C实现
- HotSpot是较新的Java虚拟机,使用JIT(Just in Time)编译器,可以大大提高Java运行的性能。
- Java原先是把源代码编译为字节码在虚拟机执行,这样执行速度较慢。而HotSpot将常用的部分代码编译为本地(原生,native)代码,这样显着提高了性能。
- HotSpot JVM 参数可以分为规则参数**(standard options)和非规则参数(non-standard options)。 规则参数相对稳定,在JDK未来的版本里不会有太大的改动。 非规则参数则有因升级JDK**而改动的可能。
1.2、JVM的架构与知识脉络图
能否画出JVM架构图?


这个架构可以分成三层看:
- 最上层:javac编译器将编译好的字节码class文件,通过java 类装载器执行机制,把对象或class文件存放在 jvm划分内存区域。
- 中间层:称为Runtime Data Area,主要是在Java代码运行时用于存放数据的,从左至右为方法区(永久代、元数据区)、堆(共享,GC回收对象区域)、栈、程序计数器、寄存器、本地方法栈(私有)。
- 最下层:解释器、JIT(just in time)编译器和 GC(Garbage Collection,垃圾回收器)
JVM有哪几块知识脉络?

2、字节码文件概述
2.1、字节码文件是跨平台的吗?
Java 虚拟机不和包括 Java 在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联。
无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。可以说,统一而强大的Class文件结构,就是Java虚拟机的基石、桥梁。

想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译为符合JVM规范的字节码。
Java 语言和虚拟机规范:https://docs.oracle.com/javase/specs/index.html

所有的JVM全部遵守Java虚拟机规范,也就是说所有的JVM环境都是一样的,这样一来字节码文件可以在各种JVM上运行。
从Java虚拟机的角度看,通过Class文件,可以让更多的计算机语言支持Java虚拟机平台。因此,Class文件结构不仅仅是Java虚拟机的执行入口,更是Java生态圈的基础和核心。
class文件里是什么?
源代码经过编译器编译之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是JVM的指令,而不像C、C++经由编译器直接生成机器码。
随着Java平台的不断发展,在将来,Class文件的内容也一定会做进一步的扩充,但是其基本的格式和结构不会做重大调整。
能介绍下生成class文件的编译器吗?

前端编译器与后端编译器
前端编译器(Front-End Compiler)
前端编译器主要负责处理源代码的语法分析、语义分析和中间代码生成。它的主要任务是将高级语言(如C、C++、Java等)编写的源代码转换为一种中间表示形式(Intermediate Representation, IR),这种中间表示形式通常是独立于目标机器的。
前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。
主要功能:
- 词法分析(Lexical Analysis):将源代码分解为一个个的词法单元(tokens),如关键字、标识符、操作符等。
- 语法分析(Syntax Analysis):根据语言的语法规则,将词法单元组织成语法树(Syntax Tree)或抽象语法树(Abstract Syntax Tree, AST)。
- 语义分析(Semantic Analysis):检查语法树的语义正确性,确保代码符合语言的语义规则,如类型检查、作用域检查等。
- 中间代码生成(Intermediate Code Generation):将语法树或抽象语法树转换为中间表示形式(如三地址码、控制流图等),这种中间表示形式通常是独立于目标机器的。
特点:
- 与源语言相关:前端编译器的设计和实现与源语言的语法和语义密切相关。
- 与目标机器无关:前端编译器生成的中间代码通常是独立于目标机器的,可以在不同的平台上运行。
后端编译器(Back-End Compiler)
后端编译器主要负责优化中间代码并生成目标机器代码。它的任务是将前端生成的中间表示形式转换为特定目标机器的机器码或汇编代码。
主要功能:
- 中间代码优化(Intermediate Code Optimization):对中间代码进行各种优化,如常量折叠、死代码消除、循环优化等,以提高代码的执行效率。
- 目标代码生成(Target Code Generation):将优化后的中间代码转换为目标机器的机器码或汇编代码。这一步需要考虑目标机器的指令集、寄存器分配、内存管理等因素。
- 机器相关优化(Machine-Specific Optimization):根据目标机器的特性进行优化,如指令选择、寄存器分配、指令调度等。
特点:
- 与目标机器相关:后端编译器的设计和实现与目标机器的指令集、体系结构密切相关。
- 与源语言无关:后端编译器通常不关心源语言的语法和语义,它只处理中间代码并生成目标机器代码。
前端与后端的协同工作
- 编译流程:前端编译器和后端编译器通常是分开的,但它们协同工作以完成整个编译过程。前端负责将源代码转换为中间表示形式,后端负责将中间表示形式转换为目标机器代码。
- 可移植性:由于中间代码是独立于目标机器的,因此可以通过更换后端编译器来生成不同目标机器的代码,从而实现代码的可移植性。
举例
- GCC(GNU Compiler Collection):GCC是一个典型的多语言编译器集合,它包含多个前端(如C前端、C++前端、Fortran前端等)和多个后端(如x86后端、ARM后端等)。不同的前端处理不同的源语言,生成统一的中间表示形式,然后由不同的后端生成目标机器代码。
- Java虚拟机(JVM):Java编译器的前端将Java源代码编译为字节码(一种中间表示形式),然后由JVM的后端(即时编译器JIT)将字节码编译为特定平台的机器码。
总结
- 前端编译器:负责源代码的词法、语法、语义分析,生成中间代码,与源语言相关,与目标机器无关。
- 后端编译器:负责中间代码的优化和目标代码生成,与目标机器相关,与源语言无关。
通过前端和后端的协同工作,编译器能够将高级语言的源代码转换为高效的目标机器代码。
前端编译器的种类
Java源代码的编译结果是字节码,那么肯定需要有一种编译器能够将Java源码编译为字节码,承担这个重要责任的就是配置在path环境变量中的javac编译器。javac是一种能够将Java源码编译为字节码的前端编译器。
HotSpot VM并没有强制要求前端编译器只能使用javac来编译字节码,其实只要编译结果符合JVM规范都可以被JVM所识别即可。
在Java的前端编译器领域,除了javac之外,还有一种被大家经常用到的前端编译器,那就是内置在Eclipse中的ECJ (Eclipse Compiler for Java)编译器。和Javac的全量式编译不同,ECJ是一种增量式编译器。
- 在Eclipse中,当开发人员编写完代码后,使用“Ctrl+S”快捷键时,ECJ编译器所釆取的编译方案是把未编译部分的源码逐行进行编译,而非每次都全量编译。因此ECJ的编译效率会比javac更加迅速和高效,当然编译质量和javac相比大致还是一样的。
- ECJ不仅是Eclipse的默认内置前端编译器,在Tomcat中同样也是使用ECJ编译器来编译jsp文件。由于ECJ编译器是釆用GPLv2的开源协议进行源代码公开,所以,大家可以登录eclipse官网下载ECJ编译器的源码进行二次开发。
- 默认情况下,IntelliJ IDEA 使用 javac 编译器。(还可以自己设置为AspectJ编译器 ajc)
javac编译器的编译步骤?
javac编译器在将Java源码编译为一个有效的字节码文件过程中经历了4个步骤,分别是词法解析、语法解析、语义解析以及生成字节码。


大部分的程序代码转换成物理机的目标代码或虚拟机能执行的指令集之前,都需要经过上图中的各个步骤。

目前前端编译器的局限性?
前端编译器并不会直接涉及编译优化等方面的技术,而是将这些具体优化细节移交给HotSpot的JIT编译器负责。
复习:AOT(静态提前编译器,Ahead Of Time Compiler)
- jdk9引入了AOT编译器(静态提前编译器,Ahead Of Time Compiler)
- Java 9 引入了实验性 AOT 编译工具jaotc。它借助了 Graal 编译器,将所输入的 Java 类文件转换为机器码,并存放至生成的动态共享库之中。
- 所谓 AOT 编译,是与即时编译相对立的一个概念。我们知道,即时编译指的是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码,并部署至托管环境中的过程。而 AOT 编译指的则是,在程序运行之前,便将字节码转换为机器码的过程。
- .java -> .class -> .so
- 最大好处:Java虚拟机加载已经预编译成二进制库,可以直接执行。不必等待即时编译器的预热,减少Java应用给人带来“第一次运行慢”的不良体验。
- 缺点:
- 破坏了java“一次编译,到处运行”,必须为每个不同硬件、OS编译对应的发行包。
- 降低了Java链接过程的动态性,加载的代码在编译期就必须全部已知。
还需要继续优化中,最初只支持Linux x64 java base
2.2、哪些类型对应有Class的对象?
(1)class:
外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
(2)interface:接口
(3)[]:数组
(4)enum:枚举
(5)annotation:注解@interface
(6)primitive type:基本数据类型
(7)void
@Test
public void test(){
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10 == c11);
}
2.3、字节码指令
什么是字节码指令(byte code)?
Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的**操作码(opcode)以及跟随其后的零至多个代表此操作所需参数的操作数(operand)**所构成。虚拟机中许多指令并不包含操作数,只有一个操作码。
比如:

为什么要懂字节码指令?
public class ByteCodeInterview {
//面试题: i++和++i有什么区别?
@Test
public void test1(){
int i = 10;
// i++;
++i;
System.out.println(i);
}
@Test
public void test2(){
int i = 10;
i = i++;
System.out.println(i);// 10
}
@Test
public void test3(){
int i = 2;
i *= i++; //i = i * i++
System.out.println(i); // 4
}
@Test
public void test4(){
int k = 10;
k = k + (k++) + (++k); //
System.out.println(k);//32
}
//包装类对象的缓存问题
@Test
public void test5(){
Integer i1 = 10;
Integer i2 = 10;
System.out.println(i1 == i2);//true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4);//false
Boolean b1 = true;
Boolean b2 = true;
System.out.println(b1 == b2);//true
}
//String声明的字面量数据都放在字符串常量池中
//jdk 6中字符串常量池存放在方法区(即永久代中)
//jdk7 及以后字符串常量池存放在堆空间
@Test
public


1428

被折叠的 条评论
为什么被折叠?



