前言:
本博客基于尚硅谷的讲师宋红康所讲的JVM教程,并对其进行了扩展,主要是介绍了几种常见的JVM虚拟机和java的体系结构。
OOM:内存溢出
write once,run anywhere;
1、跨平台的语言java和跨语言的平台
jvm:跨语言的平台,其他的有些语言经过编译器之后,生成的字节码文件,若遵循java虚拟机的规范,java虚拟机就能解释运行,也就是java虚拟机可以运行非java语言编写的程序,它只关心字节码文件。
2、字节码和多语言混合编程
3、java以及jvm历史上的重大事件
Oracl收购BEA得到JRockit虚拟机,收购Sun得到HotSpot虚拟机。
![](/qrcode.jpg)
HotSpot、JRockit、J9三大虚拟机
1996年JDK1.0提供了一个纯解释执行的虚拟机实现(Sun Classic VM)
1.Java ME、SE、EE
Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了移动终端的针对性支持,这条产品线在JDK 6以前被称为J2ME。有一点读者请勿混淆,现在在智能手机上非常流行的、主要使用Java语言开发程序的Android并不属于Java ME。
·Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这条产品线在JDK 6以前被称为J2SE。
·Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的
Java平台,除了提供Java SE API外,还对其做了大量有针对性的扩充[4],并提供了相关的部署支持,这条产品线在JDK 6以前被称为J2EE,在JDK 10以后被Oracle放弃,捐献给Eclipse基金会管理,此后被称为Jakarta EE。
2、JDK1.2重大变革
Sun在JDK1.2中将java技术体系拆分为三个方向:分别是面向桌面应用开发的J2SE(Java 2 Platform,Standard Edition)、面向企业级开发的J2EE(Java 2 Platform,Enterprise Edition)和面向手 机等移动终端开发的J2ME(Java 2 Platform,Micro Edition)。在这个版本中出现的代表性技术非常 多,如EJB、Java Plug-in、Java IDL、Swing等,并且这个版本中Java虚拟机第一次内置了JIT(Just In Time)即时编译器(JDK 1.2中曾并存过三个虚拟机,Classic VM、HotSpot VM和Exact VM,其中 Exact VM只在Solaris平台出现过;后面两款虚拟机都是内置了JIT即时编译器的,而之前版本所带的 Classic VM只能以外挂的形式使用即时编译器)
HotSpot和JRockit两者合并的结果只能说差强人意,JRockit的监控工具Java Mission Control被移植到了HotSpot,作为收费功能提供给购买了Java SE Advanced产品计划 的用户,其他功能由于两者架构的差异性明显,HotSpot能够直接借鉴融合的功能寥寥无几。
Java语言本身并不属于哪间公司所有,它由JCP组织进行管理,Oracle只是有java的商标,但是话语权很大。
JCP:java community process ,就是java社区,由业界多家技术巨头组成的社区组织,用于定义和发展java的技术规范。
ZGC,Shenandoah GC:垃圾回收器
3、java面临分裂的危机
原本JDK 9是计划在2016年发布的,但在2016年伊始,Oracle就宣布JDK 9肯定要延期至2017年,后来又连续经过了两次短时间的跳票,最终到2017年9月21日才得以艰难面世。后两次跳票的原因是以IBM和RedHat为首的十三家企业在JCP执行委员会上联手否决了Oracle提出的Jigsaw作为Java模块化 规范进入JDK 9发布范围的提。凭良心说,Java确实有模块化的刚需,不论是JDK自身(例如拆分出Java SE Embedded这样规模较小的产品)抑或是Java应用都需要用到模块化。这方面IBM本身就是各大Java发行厂商中做得最好的,它不仅让自家的JDK实现了高度模块化,还带头成立了OSGi联盟,制订了Java框架层面模块化的事实标准,所以它当然会想把OSGi推到Java规范里去争个“名份”,而不是被Jigsaw革掉“性命”。可是Oracle对此没有丝毫退让,不惜向JCP发去公开信,直言如果提案最后无法通过,那Oracle将摒弃JSR专家组,独立发展带Jigsaw的Java版本,Java顿时面临如Python 2与Python3那般分裂的危机。
4、Open JDK 和Orace JDK
其实并不是完全一致,OpenJDK中多了一个由RetHat开发的Shenandoah GC垃圾回收器
Oracle JDK是商用的,要付费,,但是个人使用的话免费。
这两个JDK共享绝大部分源码, 在功能上是几乎一样的,核心差异是前者可以免费在开发、测试或生产环境中使用,但是只有半年时间的更新支持;后者个人依然可以免费使用,但若在生产环境中商用就必须付费,可以有三年时 间的更新支持。如果说由此能得出“Java要收费”的结论,那是纯属标题党,最多只能说Oracle在迫使商业用户要么不断升级JDK的版本,要么就去购买商业支持。
4、虚拟机和java虚拟机介绍
5、JVM的位置
javac编译器将.java编译成.class文件(编译器的前端),JIT编译器是编译器的后端
6、JVM的整体结构
class文件先由Class loader 加载成大的Class对象,进入数据区,最后由执行引擎将高级语言翻译成机器语言。
操作系统只能识别机器指令。
多个线程共享堆和方法区,而另外三个每个线程独有一份
java栈现在叫做虚拟机栈
7、java代码的执行流程
解释器:保证响应时间,对字节码指令逐行执行。
二次编译:第一次将java代码编译成字节码,第二次JIT编译器将热点代码的字节码指令编译成机器指令,并缓存起来,放在方法区
8、JVM的架构模型
寄存器:是计算机中的一个高速存储单元,主要用于存储计算机程序执行过程中所需要的数据、指令地址或状态信息。它们位于中央处理器(CPU)内部,具有非常高的存取速度,对计算机的运算速度和性能有着至关重要的影响
x86的二进制指令集:是一种包含两个操作数地址的指令格式,它允许指令直接对两个操作数进行运算,并将结果存储在目的操作数指定的位置,
格式:x86的二地址指令集通常具有如下格式:操作码(OP)目的操作数(A1)源操作数(A2)。这种指令格式允许指令直接对两个操作数进行运算,并将结果存储在目的操作数指定的位置。
1、JVM为什么采用面向操作数栈而不是寄存器的架构?
解答:
Java是最早提出一次编译四处运行的计算机语言。
为了支持这种跨平台的特性,只有面向操作数栈的指令集架构才能满足需求。
2、补充
原文链接
参考链接原文链接
基于栈的指令集与基于寄存器的指令集
Java编译器输出的指令流,基本上是一种基于栈的指令集架构(Instruction Set Architecture,ISA),指令流中的指令大部分都是零地址指令,它们依赖操作数栈进行工作。
使用“基本上”,是因为部分字节码指令会带有参数,而纯粹基于栈的指令集架构中应当全部都是零地址指令,也就是都不存在显式的参数。Java这样实现主要是考虑了代码的可校验性。
与之相对的另外一套常用的指令集架构是基于寄存器的指令集,最典型的就是x86的二地址指令集,说得通俗一些,就是现在我们主流PC(Personal Computer)机中直接支持的指令集架构,这些指令依赖寄存器进行工作。
基于栈的指令集与基于寄存器的指令集有什么区别?
分别使用这两种指令集计算“1+1”的结果
iconst_1
iconst_1
iadd
istore_0
两条iconst_1指令连续把两个常量1压人栈后,iadd指令把栈顶的两个值出栈、相加, 然后把结果放回栈顶,最后istore_0把栈顶的值放到局部变量表的第0个Slot(槽位)中。
move eax, 1
add eax, 1
mov指令把EAX寄存器的值设为1,然后ad指令再把这个值加1,结果就保存在EAX寄存器里面。
两者的优缺点
基于栈的指令集主要的优点就是可移植,但是执行的速度相对较慢。
基于寄存器的指令集执行速度较快,但是寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则不可避免地要受到硬件的约束,可移植性较差。
3、什么是嵌入式?
全称:嵌入式系统
嵌入式组成:
嵌入式硬件:处理器(CPU)、存储器、外围电路
嵌入式软件:底层驱动、操作系统、应用程序
嵌入式是用于辅助、监视或者辅助操作机器和设备的装置,是一种专用的计算机系统。
特征:能够独立运行!
资源受限的平台:那些在计算、存储、带宽、能源等硬件资源或软件资源方面存在限制的平台,如嵌入式平台
9、JVM的生命周期
1、jps指令
jps指令 :jps指令可以列出当前系统中所有正在运行的Java进程的进程ID(PID)以及它们对应的主类名称或JAR文件名。这使得用户可以快速了解系统中运行的Java程序,并轻松识别各个Java应用程序的名称和PID。
2、核心概念
Object类由引导类加载器(BootStrap ClassLoader)加载,自定义的类由系统类加载器加载,系统类加载器也被称为应用程序类加载器(Application ClassLoader),父类的加载是早于子类的。
3、JVM的生命周期与启动
启动阶段:
JVM的启动是通过Bootstrap ClassLoader(引导类加载器)创建一个初始类(initial class)来完成的。这个初始类是由JVM的具体实现来指定的。
Bootstrap ClassLoader是用本地代码实现的,它负责加载核心Java类,即所有以java.*开头的类。
运行阶段:
一旦JVM启动并加载了初始类,它会初始化并调用这个类的public static void main(String[])方法,作为程序的入口点。
在执行main方法的过程中,JVM可能会链接其他类或接口,也可能会调用其他方法。
退出阶段:
JVM的退出通常发生在程序正常执行结束、遇到异常或错误而终止、某个线程调用Runtime类或System类的exit方法,或者通过JNI(Java Native Interface)规范描述的JNI Invocation API来卸载JVM时。
4、补充:单例模式
属于设计模式中的创建型模式中的一种,分为饿汉式和懒汉式等。
软件设计模式(softwaredesignpatern),又称设计模式,是一套被反复使用,多数人知晓的,经过分类编目
的,代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
1、概念
创建型模式概念:
用于描述怎样创建对象", 创建对象",它的主要特点是将对象的创建与使用分离".(也就是解耦)GoF(四人组)书中提供了单例,原型,工厂方法,抽象工场,建造者等5种创建型模式。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
单例模式(slngletonpattern)是java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式的结构:
有以下角色:
单例类:只能创建一个实例的类。
访问类:使用单例类。
2、饿汉式
定义:类加载就会导致该实例对象被创建
饿汉式实现方式一:静态成员变量
public class Singleton {
//首先有一个私有的构造方法
private Singleton (){}
//私有的静态的成员变量
private static Singleton instance = new Singleton();
//提供获取对象的方法
public static Singleton getInstance(){
return instance;
}
}
饿汉式实现方式二:静态代码块
public class Singleton {
//首先有一个私有的构造方法
private Singleton(){}
//私有的静态的成员变量,没有赋值
private static Singleton instance;
//在静态代码块中赋值
static {
instance = new Singleton();
}
//对外提供访问的方式
public static Singleton getInstance(){
return instance;
}
}
饿汉式的写法这样就可以了,那么我们来说下饿汉式的缺点吧
该方式在成员位置声明singleton类型的静态变量,并创建singleton类的对象instance.instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
3、懒汉式
定义:类加载就不会导致该实例对象被创建,而是首次使用该对象时才会创建。
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
//如果没有创建过该对象,那么直接创建,否则直接返回
//保证创建的是同一个对象
//这种方式,如果在多线程环境下是不安全的,可以加上一个synchronized锁
if (singleton != null){
singleton = new Singleton();
}
return singleton;
}
}
这里如果加上synchronized锁,可能会造成线程阻塞。
4、单例有哪些优点和缺点呢?
优点:
1、在内存里只有一个实例,减少了内存的开销,避免频繁的创建和销毁实例。
2、避免对资源的多重占用(比如写文件操作),提升了性能。
3、提供了对唯一实例的受控访问。
缺点:
1、不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3、从设计原则方面说,单例类的职责过重,在一定程度上违背了“单一职责原则”。
4、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
5、单例模式的使用场景:
1、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
2、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
10、SUN Classic VM的介绍
11、Exact VM
Exact VM的准确式内存管理
Exact VM因它使用准确式内存管理(Exact Memory Management,也可以叫NonConservative/Accurate Memory Management)而得名。准确式内存管理是指虚拟机可以知道内存中某个位 置的数据具体是什么类型。譬如内存中有一个32bit的整数123456,虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数,准确分辨出哪些内存是引用类型,这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。由于使用了准确式内存管理,Exact VM可以抛弃掉以前Classic VM基于句柄(Handle)的对象查找方式(原因是垃圾收集后对象将可能会被移动位置,如果地址为123456的对象移动到654321,在没有明确信息表明内存中哪些数据是引用类型的前提下,那虚拟机肯定是不敢把内存中所有为123456的值改成654321的,所以要使用句柄来保持引用值的稳定),这样每次定位对象都少了一次间接查找的开销,显著提升执行性能。
Classic VM的生命周期
而Classic VM的生命周期则相对要长不少,它在JDK 1.2之前是JDK中唯一的 虚拟机,在JDK 1.2时,它与HotSpot VM并存,但默认是使用Classic VM(用户可用java-hotspot参数 切换至HotSpot VM),而在JDK 1.3时,HotSpot VM成为默认虚拟机,它仍作为虚拟机的“备用选 择”发布(使用java-classic参数切换),直到JDK 1.4的时候,Classic VM才完全退出商用虚拟机的历史 舞台,与Exact VM一起进入了Sun Labs Research VM之中。
12、HotSpot VM
13、JRockit VM
Oracle将BEA的JRockit中的优秀的性能融合到HotSpot虚拟机中(HotRockit项目),比如JRockit中的Java Mission Control监控工具(故障处理套装)等功能。
14、J9
开发J9的目的是作为IBM公司各种Java产品的执行平台,在和IBM产品(如IBM WebSphere等)
搭配以及在IBM AIX和z/OS这些平台上部署Java应用。
15、KVM和CDC/CLDC HotSpot
Sun/Oracle公司所研发的虚拟机可不仅包含前面介绍到的服务器、桌面领域的商用虚拟机,面对移
动和嵌入式市场(Java ME),也有专门的Java虚拟机产品。
而在嵌入式设备上,Java ME Embedded又面临着自家Java SE Embedded(eJDK)的直接竞争和侵蚀。
16、Azul VM和Liquid VM
我们平时所提及的“高性能Java虚拟机”一般是指HotSpot、JRockit、J9这类在通用硬件平台上运行的商用虚拟机,但其实还有一类与特定硬件平台绑定、软硬件配合工作的专有虚拟机,往往能够实现更高的执行性能,或提供某些特殊的功能特性。这类专有虚拟机的代表是BEA Liquid VM和AzulVM。
Liquid VM虚拟机
Liquid VM也被称为JRockit VE(Virtual Edition,VE),它是BEA公司开发的可以直接运行在自家 Hypervisor系统上的JRockit虚拟机的虚拟化版本,Liquid VM不需要操作系统的支持,或者说它自己本身实现了一个专用操作系统的必要功能,如线程调度、文件系统、网络支持等。由虚拟机越过通用操作系统直接控制硬件可以获得很多好处,如在线程调度时,不需要再进行内核态/用户态的切换,这样可以最大限度地发挥硬件的能力,提升Java程序的执行性能。随着JRockit虚拟机终止开发,Liquid VM项目也已经停止了。
Azul VM和Zing虚拟机
Zing能让普通用户无须了解垃圾收集等底层调优,就可以使得Java应用享有低延迟、快速预热、易于监控的功能,这是Zing的核心价值和卖点,很多Java应用都可以通过长期努力在应用、框架层面优化来提升性能,但使用Zing的话就可以把精力更多集中在业务方面。
17、Apache Harmony
如果一个公司要宣称自己的运行平台“兼容于Java技术体系”,那该运行平台就必须要通过TCK(Technology Compatibility Kit)的兼容性测试,Apache基金会曾要求当时的Sun公司提供TCK的 使用授权,但是一直遭到各种理由的拖延和搪塞,直到Oracle收购了Sun公司之后,双方关系越闹越僵,最终导致Apache基金会愤然退出JCP组织,这是Java社区有史以来最严重的分裂事件之一。最后以至于连Harmony项目的最大参与者IBM公司也宣布辞去Harmony项目管理主席的职位,转而参与OpenJDK的开发。
18、Microsoft JVM 和Taobao JVM
它的主要应用之一是在 浏览器中运行Java Applets程序,微软为了在Internet Explorer 3浏览器中支持Java Applets应用而开发了自己的Java虚拟机。
java Applets应用:Java Applets是嵌入在HTML页面中的Java程序,它们通过HTML文件指定字节码和显示方式,从而实现在网页上的运行。
19、Dalivik VM
在当时性能还 不算特别强大的移动设备上,提前编译要比即时编译更容易获得高性能,所以在Android 5.0里ART就全面代替了Dalvik虚拟机。
20、可能会替换掉HotSpot的虚拟机
天下第一的位置可能会悄然更迭。