JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统

1、三大商用虚拟机

1.1 SUN公司的Hotspot VM

  • 不管是现在仍旧广泛使用的JDK6、JDK8还是JDK13默认虚拟机都是Hotspot。当面试的时候,让你说说JVM/java虚拟机,都是默认让你讲讲Hotspo。
    如果还反问面试官:您是让我讲SUN Classic还是Exact?
    面试官:那就都讲讲吧(手动滑稽);无异于是给自己挖坑!
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第1张图片
  • 名称中Hotspot指的是它的热点代码探测技术:通过计数器找到最具有编译价值的代码,触发即时编译器(JIT)或栈上替换(Exact VM初具热点探测的雏形,但不久就被Hotspot替换
  • Hotspot有方法区,而JRockit和J9都没有方法区
  • 服务端、桌面端、移动端、嵌入式都有应用

1.2 BEA公司的JRockit

  • 专注于服务器端应用,可以不太关注程序启动速度,因此JRockit内部不包含解释器,全部代码都靠即时编译器编译执行
    关于解释器和即时编译器多说几句:解释器就是边翻译边执行,无论什么样的代码,是否有重复部分,都要翻译一遍再执行,执行效率较低,即使编译器能够探测热点代码,只要编译过一次的代码,下次JVM再遇到它,编译执行效率就比第一次遇到它的时候快很多;给用户的体验就是:只有解释器,应用运行起来就是卡顿,反应很慢
  • 号称世界上最快的Java虚拟机(J9也号称最快,但是还是有点区别)
  • 2008年BEA被Oracle收购

1.3 IBM公司的J9

  • 市场定位与Hotspot接近,服务端、桌面、嵌入式都有应用
  • 在IBM自己的Java产品中,号称世界上最快的Java虚拟机,可以这么理解:ios在苹果自己的硬件上很快,而强行装到其他配置更高的Android设备,反而一堆BUG
  • 2017年,IBM发布了Open J9,交给Eclipse基金会管理

放个JVM内存结构图在这儿
JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第2张图片
JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第3张图片

2、类加载子系统

2.1类加载器加载阶段:

  • Car.class存放在硬盘中,被ClassLoader(具体是引导类ClassLoader还是扩展类ClassLoader还是系统类ClassLoader)后面细说
  • 加载之后,就生成一个叫做Car的Class对象存放在堆中(学过反射的应该都知道),它的元数据:方法代码、访问权限、变量名等存放在方法区(看上面那个图,方法区在运行时数据区里,java8以后,方法区又叫元空间)
  • Car的Class对象可以调用Car的构造方法进行实例化,创建出一个个的car实例:car1,car2……并将这些car实例也存放在堆里
  • 这些堆中的car实例通过getClass方法可以获取到Car这个类本身(谁创建了堆里的这些car)
  • Car的Class实例可以调用getClassLoader方法获取到加载Car这个类的ClassLoader(谁加载了Car.class)
  • .class文件–>JVM–>最终元数据模板,这个过程中ClassLoader扮演的是一个快递员的角色,ClassLoader是通过二进制流的方式来传输数据到内存的
  • 下面给出一张图描述上述过程:
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第4张图片
  • 总结类加载器子系统的加载阶段(还有链接和初始化阶段没说呢)
    1、通过一个类的全限定名获取定义此类的二进制字节流
    2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3、在堆中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  • 补充:加载.class文件的方式(我感觉这个不是很重要,就作为一个了解吧)
    1、本地系统直接加载
    2、通过网络获取:Web Applet
    3、从zip压缩包中读取、ar、war包
    4、运行时计算生成:动态代理技术
    5、由其他文件生成,如JSP应用
    6、从专有的数据库提取
    7、从加密的文件中获取,防止class文件被反编译的保护措施

2.2类加载器链接阶段

  • 直接上图,并作下解释说明:
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第5张图片
  • 关于验证阶段,举个例子,.class文件在文件开头都有特定的文件标识,用Binary Viewer随便打开一个.class文件
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第6张图片
    发现它的开头是CAFEBABY(俗称咖啡宝贝),所有的java的.class文件开头都是如此,这就是一个简单的验证过程。
  • 准备阶段就是初始化所有的静态变量,例如字符类型就赋\u0000,布尔类型就赋false,引用类型就赋null
  • 解析阶段,以System.out.println();为例,常量池中会有:
#37 = Utf8		com/demo/Hello
#38 = Utf8		java/lang/Object
#39 = Utf8		java/lang/System
#40 = Utf8		out
#41 = Utf8		Ljava/io/PrintStream;
#42 = Utf8		java/io/PrintStream
#43 = Utf8		println
从以上反编译得到的结果可以看出Object、PrintStream等这些类都需要去类库加载,
所以需要一个直接引用从你写的.class文件到java类库

2.3类加载器初始化阶段

  • 什么是初始化阶段
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第7张图片
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第8张图片如图,并没有定义方法,只要有静态变量,静态代码块声明,就会自动创建一个方法
    反之,则不创建

2.4加载阶段的几种类加载器详解

  • 前面提到了三种类加载器:引导类加载器(Bootstrap Class Loader)扩展类加载器(Extension Class Loader)系统类加载器(System Class Loader),而根据《Java虚拟机规范》JVM支持的只有引导类加载器自定义类加载器,这三种和两种之间到底是什么关系呢?
    《Java虚拟机规范》定义的是:将所有派生于抽象类ClassLoader的类加载器划分为自定义类加载器。也就是说:扩展类加载器和系统类加载器都间接继承了ClassLoader这个类(这个可以去看源码),因此它们俩也算是自定义类加载器,如下继承图:
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第9张图片
    因此前面的三种类加载和这里说的两种类加载器关系就应该是这样的:
    JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第10张图片
  • 类加载器的一个Demo
public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader=ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d
        //获取系统类加载器上层:扩展类加载器
        ClassLoader exClassLoader = systemClassLoader.getParent();
        System.out.println(exClassLoader);//jdk.internal.loader.ClassLoaders$PlatformClassLoader@723279cf
        //获取扩展类加载器上次:引导类加载器
        ClassLoader bootClassLoader = exClassLoader.getParent();
        System.out.println(bootClassLoader);//null
        //对用户自定义类来说:默认使用系统类加载器加载
        ClassLoader userClassLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(userClassLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d
        //尝试得到String类的类加载器
        System.out.println(String.class.getClassLoader());//null
    }
}
从上面的代码,我们可以看到逐层往上获取类加载器,最上层的引导类加载器是获取不到的,为null。
并且,用户自己创建的类,在类加载阶段,用的是三个类加载中的系统类加载器;
而加载Java核心类库的时候,都用的是引导类加载器加载的;
为什获取引导类加载器是null呢?因为引导类加载器是C/C++写的,我们也是拿不到它的对象的

JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第11张图片
JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第12张图片
JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统_第13张图片

你可能感兴趣的:(JVM详解学习笔记(二)之三大商用虚拟机&类加载器子系统)