JVM

Java从编码到执行

image-20220328105816911

Java是一种编译与解释都有的编程语言,其中大部分的代码时通过编译为字节码执行的,而一些特定的次数较多的代码将使用JIT做本地编译,直接交给操作系统调用。

编译器可以修改编译模式,共三种:

  1. -Xint,纯解释模式,编译极快,启动较慢

  2. -Comp,纯编译模式,编译很慢,启动很快

  3. -Xmixed,混合模,开始解释执行,启动速度较快,对热点代码实时检测和编译

    1. 多次被调用的方法

    2. 多次被调用的循环

什么是JVM

JVM是一种跨语言的平台,是一种规范。可以在JVM上运行的语言多大一百多种。JVM本身不是跨平台的,每种操作系统都有对应的实现。只要编程语言可以编译为class文件,就可以运行在JVM中。

image-20220328110245015

可以使用 java -version 命令查看当前使用的jvm类型,目前市面上主要的集中jvm实现有:

  • Hotspot,Oracle官方提供

  • Jrockit,BEA公司,曾号称最快的JVM,现被Oracle收购,合并到Hotspot中

  • J9,IBM公司

  • Microsoft VM

  • TaobaoVM

  • LiquidVM,普通虚拟机运行于操作系统上,而LiquidVM直接运行在物理机上,作为一个操作系统

  • zing,azul 公司,商业软件,垃圾回收业界标杆(Hotspot参考了该虚拟机,提供了新的ZGC)

Class文件格式

查看Class文件的16进制形式,使用IDEA插件BinEd,打开.class文件并使用如下操作:

image-20220328114011149

有如下类:

他的字节码信息如下;

![马士兵教育 java1.8类文件格式第一版](README.assets/马士兵教育 java1.8类文件格式第一版.png)

使用javap命令查看class文件信息

使用IDEA Show ByteCode 查看class文件信息

image-20220328135943224
image-20220328140032276

在IDEA内部配置javap工具

image-20220328151851908

重启IDEA:

image-20220328151152648

类加载机制

image-20220328154802361

一个class文件被初始化到JVM通常要经过下面几个过程:

  1. Loading,将class加载到内存中

  2. Linking,链接

    1. Verification,校验字节码

    2. Preparation,将class文件的静态变量赋默认值(注意不是代码设置的初始值)

    3. Resolution,class文件中使用的常量池的符号引用转换为内存地址

  3. Initializing,静态变量赋代码设置的初始值,调用静态代码块

1. Loading:ClassLoader

加载Class文件到内存的步骤由ClassLoader来完成:

image-20220328161801013

可以在java代码中通过如下方式查询类加载器之间的关系,以及当前类是由哪个类加载器加载进来的:

双亲委派机制

  1. 类加载符合双亲委派机制

  2. 当一个class需要被load到内存时,会自顶向上寻找class是否已经被load到内存了:CustomClassLoader、AppClassLoader、ExtensionClassLoader、BootstrapClassLoader的顺序

  3. 如果某个ClassLoader已经加载这个类,那么会直接返回

  4. 如果直到BootstrapClassLoader都没有加载这个类,那么Bootstrap会尝试load这个class

  5. 这个load的操作会自顶向上的进行,会以 BootstrapClassLoader、ExtensionClassLoader、AppClassLoader、CustomClassLoader的顺序进行load

双亲委派就是从子到父的查找过程,又有一个从父到子的load过程

为什么需要双亲委派?

  1. 主要是为了安全问题:如果没有双亲委派,假设第三方代码重写JDK中的核心类,对引用第三方代码的java程序造成破坏

  2. 次要问题:可以解决类加载缓存的问题,放置二次加载损耗资源

使用类加载器

什么时候需要手动加载一个类:

  1. 动态代理生成了一个新的class,此时需要程序手动load

  2. 目标class在网络上,需要先下载,再手动load

  3. 热部署加载,class文件修改后,手动load

使用加载器加载一个资源为流:

loadClass的源码

findInCache -> parent.loadClass -> findClass()

自定义类加载器

继承ClassLoader类并重写loadClass方法, 参考类sun.misc.Launcher.AppClassLoader

2. Linking

2.1 Verification

验证文件是否符合JVM规定

2.2 Preparation

静态成员变量赋默认值

2.3 Resolution

将类、方法、属性等符号引用解析为直接引用 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

3. Initialzing

调用初始化代码

指令重排序

有如下代码:

他的main方法生成的指令如下:

因为有可能发生指令重排序,他的字节码指令有可能是如下这样的(astore_1 和 invokespecial有可能被重排):

所以也就有了双重检查锁的问题:

JMM

Java Memory Model,Java内存模型。

硬件层的数据一致性

image-20220329145137738

因为CPU、内存、磁盘的读取速度各不相同,所以存储器按照他们的速度将其划分为了6个层次。当CPU想要读取某个数据时,会先从寄存器开始读取,依次向下查找;如果找到了,会从下到上依次缓存到更高级别的缓存。是一种多级缓存的机制。

最后更新于

这有帮助吗?