不动笔墨不读书,先把书读厚,再把书读薄是我学习方式。
所以等理解了再整理一次笔记,目前笔记和视频一一对应。
笔记连载中
《尚硅谷2020最新版宋红康JVM》
对 Java 程序员来说,Java 虚拟机(JVM )可以说是既熟悉又神秘,很少有 Java 程序员能够抑制自己探究它的冲动。可惜透彻讲解 JVM 的书籍(尤其是国内出版的)简直少之又少,这里就更不要说能系统讲解 JVM 的视频教程了。尚硅谷的这套 JVM 从入门到精通,绝对可谓是研究 JVM 的程序员及广大 JVM 爱好者的福音。
# JDK1.8 目前企业用的比例最高的JDK,本课程基于java8
# JClassLib_windows.zip 用它读懂字节码文件
# MemoryAnalyzer-1.9.0.20190605-win32.win32.x86_64.zip 内存分析工具
# PXBinaryViewerSetup.exe 读取字节码的软件
java虚拟机规范可以去官网下载,也可以去买jvm相关的书。
作为 Java 工程师的你曾被 JVM 伤害过吗?你是否也遇到过这些问题?
大部分Java开发人员,除了会在项目中使用到java平台相关的高精尖技术,对与Java技术的核心Java虚拟机了解很少
开发人员如何看待上层框架?
一些有一定工作经验的开发人员,打心底觉得SSM,微服务等上层技术才是重点,基础技术并不重要,这其实是一种本末倒置的“病态”。
如果我们把核心类库的API比作数学公式的话,那么Java虚拟机的知识就好比公式的推导过程
计算机系统体系对我们来说越来越远,在不了解底层实现方式的前提下,通过高级语言很容易编程程序代码。但事实上计算机并不认识高级语言
架构师每天都在思考什么?
为什么要学习JVM?
Java对比C++
java抛弃了C/C++的两大缺点,Java可以内存动态分配,有垃圾回收技术;
自动分配的内存要想再次使用就需要用到垃圾回收技术
垃圾回收机制为我们打理了很多繁琐的工作,大大提高开发的效率,但是垃圾回收机制不是万能的,
懂得JVM内部的内存结构,工作机制,是设计高扩展性应用和诊断运行时问题的基础,
也是java开发工程师进阶的必备能力。
先学习怎么使用,再去了解原理是什么!别一开始就打消了积极性。
调优在课程第三篇会讲,会用到很多具体的监控工具
调优:代码层面、内存层面
代码层面i++,++i有何区别?
我们需要把代码编译成字节码文件,然后再反编译才能解释区别。
Java语言规范和JVM规范,推荐看8或者11,可以去官网下载https://docs.oracle.com/javase/specs/index.html
英语薄弱者请无视。
不推荐买Java虚拟机规范这本书,规范未必能看得懂。推荐买深入理解Java虚拟机,我们要买的是规范的解读。
JVM规范是Oracle公司发布的标准,随着JDK的不断更新,JVM规范也在不断迭代。规范是虚的,属于天上飞的理念,落地的实现是具体不同版本的Java虚拟机。JVM规范有很多版本,java虚拟机也有很多版本,我们要学的是官方正统的HotSpot虚拟机.
世界上没有最好的语言,只有最适用于具体应用场景的编程语言。Java目前仍是世界上最流行的编程语言。
Java 不再是简简单单的一门计算机语言了,它更是一个平台,一种文化,一个社区,形成了庞大的生态。
java的特点之一:跨平台
Java的官网介绍
The Java Virtual Machine is the cornerstone of the Java platform. It is thecomponent of the technology responsible for its hardware- and operating system-independence, the small size of its compiled code, and its ability to protect usersfrom malicious programs…
Java虚拟机是Java平台的基石。它是该技术的组成部分,负责其硬件和操作系统的独立性,以及负责编译后代码的小尺寸,它具有保护用户免受恶意程序攻击的能力…
Java虚拟机根本不关心运行在其内部的程序到底是使用哪种语言编写的,它只关心字节码文件。只要编译后的字节码文件符合Java虚拟机的规范,它就是一个有效的字节码文件,就能被Java虚拟机识别并装载运行。
Java语言不是最强大的语言,但是jvm是最强大的虚拟机。
信息产业三大难题:CPU,操作系统,编译器
Java平台上的多语言混合编程正成为主流,通过特定领域的语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。
试想一下,在一个项目之中,并行处理用clojure语言编写,展示层使用JRuby/Rails,,中间层则是Java,每个应用层都将使用不同的编程语言来完成,而且,接口对每一层的开发者都是透明的,各种语言之间的交互不存在任何困难,就像使用自己语言的原生API一样方便,因为它们最终都运行在一个虚拟机之上。
对这些运行于Java虚拟机之上、Java之外的语言,来自系统级的、底层的支持正在迅速增强,以JSR-292为核心的一 系列项目和功能改进(如DaVinci Machine项目、 Nashorn引擎、InvokeDynamic指令、java. lang. invoke包等),推动Java虚拟机从Java语言的虚拟机向多语言虚拟机的方向发展。
如何真正搞懂jvm?
Java虚拟机非常复杂,要想真正理解它的工作原理,最好的方式就是实践!自己动手编写一个jvm!
1990年,在Sun公司中,由JamesGosling领导的小组Green Team,开发出的新的程序语言,命名为Oak,后期命名为Java
1995年,Sun正式发布Java和HotJava产品,Java 首次公开亮相。
1996年1月23日Sun公司发布了JDK 1.0。
1998年,JDK 1.2版本发布。同时,Sun发布了jsp/Servlet、EJB规范,以及将Java分成了J2EE、 J2SE和J2ME。这表明 了Java开始向企业、 桌面应用和移动设备应用三大领域挺进。
2000年,JDK 1.3发布,Java HotSpot Virtual Machine正式发布,成为Java的默认虚拟机。
2002年,JDK 1.4发布,古老的Classic虚拟机退出历史舞台。
2003年年底,Java平台的Scala正式发布,同年Groovy也加入了Java阵营。
2004年,JDK 1.5发布。同时JDK 1. 5改名为JavaSE 5.0。
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。同年,IBM的J9开源,形成了现在的Open J9社区
2018年,Android的Java侵权案判决,Google赔偿Oracle计88亿美元
同年,Oracle宣告JavaEE成为历史名词,JDBC、 JMS、Servlet赠予Eclipse基金会
同年,JDK11发布,LTS版本的JDK,发布革命性的ZGC,调整JDK授权许可
2019年,JDK12发布,加入RedHat领导开发的Shenandoah GC
三大商用虚拟机:sun系列的HotSpot、JRockit虚拟机、IBM J9虚拟机
Open JDK和Oracle JDK
在JDK11之前,OracleJDK中还会存在一些openJDK中没有的、闭源的功能。但在JDK11中,除了协议不同之外,
我们可以认为open JDK和Oracle JDK代码实质上已经完全一致的程度 。
Java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。
每一条Java指令,Java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里。
JVM是运行在操作系统之上的,它与硬件没有直接的交互。
JDK,JRE,JVM三者关系图:
前端编译器:比如Javac,它能把.java文件转变成.class字节码文件
后端运行期编译器(JIT编译器,Just In Time Compiler):把字节码转成机器码
HotSpot VM是目前市面上高性能虚拟机的代表作之一。它采用解释器与即时编译器并存的架构。
在今天,Java程序的运行性能早已脱胎换骨,已经达到了可以和C/C+ +程序一较高下的地步。
类加载器子系统把字节码文件装载到内存中,并生成Class对象,涉及到加载,链接,初始化。
多线程共享方法区和堆;java栈,本地方法栈,程序计数器(pc寄存器)每个线程独有一份
执行引擎里分三部分:解释器,JIT编译器,垃圾回收器。操作系统只能识别机器指令,但字节码≠机器指令
要想执行JVM字节码文件,必须借助执行引擎。执行引擎充当了高级语言→机器语言的翻译者。
不需要关注前端编译器,除非你要开发一门新的语言,并借助jvm解释运行。
只要让字节码文件符合jvm规范,就可以在jvm上解释执行,即使它不是Java语言开发的。
市面上主流的虚拟机采用解释器与即时编译器并存的方式
当程序需要迅速启动运行的时候,解释器可以先发挥作用,省去编译的时间,立即执行。
热点代码:当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”,为了提高其执行效率,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化。完成这个任务的编译器叫做即时编译器(Just In Time Compiler —— JIT编译器)。
Java编译器输入的指令流基本上是一种基于栈的指令集架构,另外一种指令集架构则是基于寄存器的指令集架构。
Hotspot虚拟机只有一种寄存器:pc寄存器,所以它是基于栈的指令集架构。
具体来说:这两种架构之间的区别:
基于栈式架构的特点
➢设计和实现更简单,适用于资源受限的系统;
➢避开了寄存器的分配难题:使用零地址指令方式分配。
➢指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。指令集更小,编译器容易实现。.
➢不需要硬件支持,可移植性更好,更好实现跨平台
基于寄存器架构的特点
➢典型的应用是x86的二进制指令集:比如传统的PC以及Android的Davlik虚拟机
➢指令集架构则完全依赖硬件,可移植性差
➢性能优秀和执行更高效;
➢花费更少的指令去完成一项操作。
➢在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主。
栈式架构它是零地址指令,每八位进行对齐,每八位为基本单位。指令集更小,但是指令更多
寄存器架构它是每16位为基本单位(双字节方式进行对齐)
public class StackStruTest {//这是java源文件,字节码文件我们程序员看不懂,需要进行反编译
public static void main(String[] args) {
int i = 2;
int j = 3;
int k = i + j;
}
}
IDEA命令行中使用javap -v反编译字节码文件,可以看出,执行2+3需要八行代码
//这是StackStruTest.class字节码文件通过反编译截取的一部分代码
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code: //执行的相应指令
stack=2, locals=4, args_size=1
//左边的0-8是pc寄存器识别的地址
0: iconst_2 //生成常量2
1: istore_1 //保存到1的操作数栈中,1是操作数栈的索引位置
2: iconst_3 //生成常量3
3: istore_2 //保存到2的操作数栈中
4: iload_1 //加载
5: iload_2 //加载
6: iadd //相加
7: istore_3 //k保存到3的操作数栈中
8: return
只需要两行代码
mov eax,2 //将eax寄存器的值设为2
add eax,3 //使eax寄存器的值加3
由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。
优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的
具体实现指定的。(由jvm规范定义的,具体实现由具体的jvm虚拟机实现)
Java虚拟机何时启动?自定义类的执行过程是怎样的?
自定义类要执行→父类等必需的类还没加载(Object类等核心api需要由引导类加载器加载)→Java虚拟机启动→创建一个初始类
→调用初始类内部的main方法→加载类
一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序
程序开始执行时他才开始执行,程序结束时他就停止
执行一个所谓的Java程序的时候,真真正正在执行的是一个叫做Java虚拟机的进程
有如下几种情况
热点代码:当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”,为了提高其执行效率,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并放在方法区缓存起来。
Exact VM
为了解决Classic VM问题,jdk1.2时,sun提供了此虚拟机。
Exact Memory Management:准确式内存管理
➢也可以叫Non-Conservative/Accurate Memory Management
➢虚拟机可以知道内存中某个位置的数据具体是什么类型。
具备现代高性能虚拟机的雏形
➢热点探测
➢编译器与解释器混合工作模式
只在Solaris平台短暂使用,其他平台上还是classic vm
➢英雄气短,终被Hotspot虚拟机替换
SUN公司的HotSpot VM
本课程默认介绍的虚拟机都是HotSpot虚拟机,面试默认问的就是HotSpot。面试时少提另外两个商用虚拟机,别给自己挖坑
在jdk7及以前,习惯上把方法区称为永久代,jdk8开始,使用元空间取代了永久代。
不过元空间与永久代最大的区别在于:元空间不再虚拟机设置的内存中,而是使用本地内存
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。JRockit/J9中不存在永久代的概念
BEA的JRockit
专注于服务器端应用T
它可以不太关注程序启动速度,因此JRockit内部不包含解析器实现,全部代码都靠即时编译器编译后执行。
大量的行业基准测试显示,JRockit JVM是世界上最快的JVM。
优势:全面的Java运行时解决方案组合
JRockit面向延迟敏感型应用的解决方案JRockit Real Time提供以毫秒或微秒级的JVM响应时间,
适合财务、军事指挥、电信网络的需要
MissionControl服务套件,它是一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。
2008年,BEA被Oracle收购
Oracle表达了整合两大优秀虚拟机的工作,大致在JDK 8中完成。整合的方式是在HotSpot的基础上,移植JRockit的优秀特性。
高斯林:目前就职于谷歌,研究人工智能和水下机器人
KVM和CDC/CLDC Hotspot
Oracle在Java ME产品线上的两款虚拟机为: CDC/CLDC HotSpot Implementation VM
KVM [Kilobyte)是CLDC-HI早期产品
目前移动领域地位尴尬,智能手机被Android和ios二分天下。
KVM简单、轻量、高度可移植,面向更低端的设备上还维持自己的一片市场.
➢智能控制器、传感器
➢老人手机、经济欠发达地区的功能手机
所有的虚拟机的原则:一次编译,到处运行。
前面三大“高性能Java虚拟机”使用在通用硬件平台上
这里Azul VM和BEA Liquid VM是与特定硬件平台绑定、软硬件配合的专有虚拟机,高性能Java 虚拟机中的战斗机。
Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于Azul systems公司的专有硬件Vega系统上的Java虚拟机。
每个Azul VM实例都可以管理至少数十个CPU和数百GB内存的硬件资源,并提供在巨大内存范围内实现可控的GC时间的垃圾
收集器、专有硬件优化的线程调度等优秀特性。
2010年,Azul Systems公司开始从硬件转向软件,发布了自己的Zing JVM,
可以在通用x86平台上提供接近于vega系统的特性。
高性能Java虚拟机中的战斗机。BEA公司开发的,直接运行在自家Hypervisor系统
Liquid VM即是现在的JRockit VE (Virtual Edition) , Liquid VM不需要操作系统的支持,
或者说它自己本身实现了一个专用操作系统的必要功能,如线程调度、文件系统、网络支持等。
随着JRockit虚拟机终止开发,Liquid VM项目也停止了。
Apache也曾经推出过与JDK 1. 5和JDK 1. 6兼容的Java运行平台Apache Harmony。
它是IBM和Intel联合开发的开源JVM,受到同样开源的0penJDK的压制,Sun坚决不让Harmony获得JCP认证,
最终于2011年退役,IBM转而参与OpenJDK
虽然目前并没有Apache Harmony被大规模商用的案例,但是它的Java类库代码吸纳进了Android SDK。
微软为了在IE3浏览器中支持Java Applets,开发了Microsoft JVM。只能在window平台下运行。
但确是当时windows下性能最好的Java VM。
1997年,Sun以侵犯商标、不正当竞争罪名指控微软成功,赔了sun很多钱。
微软在WindowsXP SP3中抹掉了其VM。现在windows上安装的jdk
由AliJVM团队发布。阿里,国内使用Java最强大的公司,覆盖云计算、金融、物流、电商等众多领域,
需要解决高并发、 高可用、分布式的复合问题。有大量的开源产品。
基于OpenJDK开发了自己的定制版本AlibabaJDK简称AJDK。是整个阿里Java体系的基石。
基于OpenJDK HotSpot VM发布的国内第一个优化、深度定制且开源的高性能服务器版Java虚拟机。
创新的GCIH (GC invisible heap )技术实现了off-heap ,即将生命周期较长的Java对象从heap中移到heap之外,
并且Gc不能管理GCIH内部的Java对象,以此达到降低GC的回收频率和提升GC的回收效率的目的。
GCIH中的对象还能够在多个Java虚拟机进程中实现共享
使用crc32指令实现JVM intrinsic 降低JNI的调用开销
PMU hardware 的Java profiling tool和诊断协助功能
针对大数据场景的ZenGC
taobao vm应用在阿里产品上性能高,硬件严重依赖intel的cpu,损失了兼容性,但提高了性能.
未来想学技术,看国内BAT等大厂用什么技术,朝这个方向努力错不了
谷歌开发的,应用于Android系统,并在Android2.2中提供了JIT,发展迅猛
Dalvik VM只能称作虚拟机,而不能称作“Java虚拟机”,它没有遵循Java虚拟机规范,不能直接执行Java 的Class 文件
基于寄存器架构,不是jvm的栈架构。
执行的是编译以后的dex (Dalvik Executable) 文件。执行效率比较高。
它执行的dex (Dalvik Executable) 文件可以通过Class文件转化而来,
使用Java语法编写应用程序,可以直接使用大部分的Java API等。
Android 5.0 使用支持提前编译(Ahead Of Time Compilation, AOT)的ART VM替换Dalvik VM。(原文件→机器指令)
jvm还有很多,其他的不做介绍了
具体JVM的内存结构,其实取决于其实现。不同厂商的JVM,或者同一厂商发布的不同版本,都有可能存在一定差异。
本套课程主要以Oracle HotSpot VM为默认虚拟机。
2018年4月,Oracle Labs公开了Graal VM, 号称"Run Programs Faster Anywhere",勃勃野心。
与1995年java的”write once, run anywhere "遥相呼应。
GraalVM在HotSpotVM基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用。语言包括: Java、Scala、
Groovy、Kotlin; C、C++、JavaScript、Ruby、Python、 R等
支持不同语言中混用对方的接口和对象,支持这些语言使用已经编写好的本地库文件
工作原理是将这些语言的源代码或源代码编译后的中间格式,通过解释器转换为能被Graal VM接受的中间表示。
Graal VM提供Truffle工具集快速构建面向一种新语言的解释器。在运行时还能进行即时编译优化,获得比原生编译器
更优秀的执行效率。
如果说HotSpot有一天真的被取代,Graal VM希望最大。但是Java的软件生态没有丝毫变化。
class文件通过类加载器子系统加载进内存中,执行引擎根据字节码指令依次执行
如果自己手写一个Java虚拟机的话,主要考虑哪些结构呢?
类加载器和执行引擎
类加载器:获取类的信息并装进内存
执行引擎:逐条解释指令
1.类加载器子系统从文件中读取.class文件加载进内存中并生成Class对象
2.在字节码反编译后可以看到Constant pool(常量池),加载进内存中称为运行时常量池
public class HelloLoader {
public static void main(String[] args) {
System.out.println("谢谢ClassLoader加载我....");
System.out.println("你的大恩大德,我下辈子再报! ");
}
}
当执行HelloLoader的main方法时,过程如图: