JVM 类加载

​​​​​​​

1.静态Class文件结构

1.1 javac  和 javap  的区别

javac 命令用于将 Java 源文件编译为字节码文件。字节码文件是 Java 程序在运行时需要的文件。

javap 命令用于反编译字节码文件,将其转换为 Java 源代码。

javac 命令的语法如下:

javac [options] source_files

options 选项可以指定编译器的行为。常用的选项包括:

  • -d:指定生成字节码文件的路径。
  • -cp:指定编译时使用的类路径。
  • -sourcepath:指定源文件所在的目录。
  • -target:指定生成字节码文件的目标版本。

javap 命令的语法如下:

javap [options] class_files

options 选项可以指定反编译器的行为。常用的选项包括:

  • -c:将字节码文件转换为 Java 源代码。
  • -s:显示字节码文件的常量池信息。
  • -v:显示字节码文件的详细信息。

javacjavap 命令的示例:

# 编译 Java 源文件
javac HelloWorld.java

# 反编译字节码文件
javap -c HelloWorld.class

1.2 JVM C1,C2编译器以及分层编译

基本功 | Java即时编译器原理解析及实践 - 美团技术团队

C1编译器

C1编译器是一个简单快速的编译器,主要的关注点在于局部性的优化,适用于执行时间较短或者对启动性能有要求的程序,也称Client Compiler,例如,eclipse 或者 idea中的 局部编译。

C2编译器

C2编译器是为长期运行的服务器端应用程序做性能调优的编译器,适用于执行时间较长或者对峰值性能有要求的程序,也称为Server Compiler,例如,服务器上长期运行的Java应用对稳定运行就有一定的要求。

Graal Compiler

从JDK 9开始,Hotspot VM中集成了一种新的Server Compiler,Graal编译器。JVM会在解释执行的时候收集程序运行的各种信息,然后编译器会根据这些信息进行一些基于预测的激进优化,比如分支预测,根据程序不同分支的运行概率,选择性地编译一些概率较大的分支。Graal比C2更加青睐这种优化,所以Graal的峰值性能通常要比C2更好。

Graal编译器可以通过Java虚拟机参数-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler启用。当启用时,它将替换掉HotSpot中的C2编译器,并响应原本由C2负责的编译请求。
 

分层编译

在Java7之前,需要根据程序的特性来选择对应的JIT,虚拟机默认采用解释器和其中一个编译器配合工作。

Java7引入了分层编译,这种方式综合了C1的启动性能优势和C2的峰值性能优势,我们也可以通过参数-client或者-server强制指定虚拟机的即时编译模式。

分层编译将JVM的执行状态分为5个层次:

第0层:程序解释执行,默认开启性能监控功能(Profiling),如果不开启,可触发第二层编译;
第1层:可称为C1编译,将字节码编译为本地代码,进行简单、可靠的优化,不开启Profiling;
第2层:也称为C1编译,开启Profiling, 仅执行带方法调用次数和循环回边执行次数profiling的C1编译;
第3层:也称为C1编译,执行所有带Profiling的C1编译;
第4层:可称为C2编译,也是将字节码编译为本地代码,但是会启用一些编译耗时较长的优化,甚至会根据性能监控信息进行一些不可靠的激进优化。 

1.3. Class文件结构

官方文档位置:
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

Class文件格式采用一种类似于C语言结构体的方式进行数据存储,这种结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8 来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8编码构成字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次
关系的复合结构的数据,整个class 文件本质上就是一张表。由于表没有固定长度,所以通常会在其前面加上个数说明

1.3.1 魔数

Class文件的标志Magic Number(魔数)
每个Class 文件开头的4个字节的无符号整数称为魔数(Magic Number)

它的唯一作用是确定这个文件是否为一个能被虚拟机接受的有效合法的Class文件。即:魔数是Class文件的标识符。
魔数值固定为OxCAFEBABE。不会改变。

如果一个Class文件不以0xCAFEBABE开头,虚拟机在进行文件校验的时候就会直接抛出以下错误:
Error: A JNI error has occurred,please check your installation and try again
Exception in thread “main” java.lang.ClassFormatError: Incompatible magic value 1885430635 in classfile StringTest

使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为文件扩展名可以随意地改动。

1.3.2 Class文件版本号

紧接着魔数的4个字节存储的是Class 文件的版本号。同样也是4个字节。第5个和第6个字节所代表的含义就是编译的副版本号minor_version,而第7个和第8个字节就是编译的主版本号major_version。
它们共同构成了class文件的格式版本号。譬如某个Class 文件的主版本号为M,副版本号为 m,那么这个Class 文件的格式版本号就确定为M.m。

1.3.3 常量池

常量池:存放所有常量
常量池是Class文件中内容最为丰富的区域。常量池对于Class文件中的字段和方法解析也有着至关重要的作用。随着Java虚拟机的不断发展,常量池的内容也日渐丰富。可以说,常量池是整个Class文件的基石。

在版本号之后,紧跟着的是常量池的数量,以及若干个常量池表项。
常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的无符号数,代表常量池容量计数值(constant_pool_count)。与Java中语言习惯不一样的是,这个容量计数是从1而不是0开始的。
常量池表
constant_pool (常量池)
constant_pool是一种表结构,以1~ constant_pool_count - 1为索引。表明了后面有多少个常量项。·常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)
它包含了class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其他常量。常量池中的每一项都具备相同
的特征。第1个字节作为类型标记,用于确定该项的格式,这个字节称为tag byte (标记字节、标签字节)。

1.3.4 访问标识
1.3.5 类索引、父类索引、接口索引集合
1.3.6 字段表集合
1.3.7 方法表集合
1.3.8 属性表集合

2.字节码指令集与解析举例

3.类加载的过程

1.加载

1.1类加载(3中JVM系统的类加载器(rt,ext,application),用户自定义的)

1.2 双亲委派机制 (1.类在系统中只有一份,避免重复加载,2.安全性)

1.3 创建一个 类的 几种方法

new,反射,反序列化字节码,

1.3.1 使用 new 关键字

这是创建 Java 类的最常见方式。使用 new 关键字可以创建一个类的实例,该实例是该类的对象

1.3.2  使用 Class 类的 newInstance() 方法

Class 类是 Java 中所有类的类。newInstance() 方法可以创建一个类的实例,该实例是该类的对象。

Java

public class Person {

    public void sayHello(String name) {
        System.out.println("Hello, my name is " + name);
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.newInstance();
        person.sayHello();
    }
}
1.3.3使用 反射 机制

反射机制可以访问类的内部信息,包括类的构造方法、字段和方法。使用反射机制可以创建一个类的实例,该实例是该类的对象。

1.3.4 使用 序列化 机制

序列化机制可以将对象转换为字节流,然后将字节流保存到文件中。可以从文件中读取字节流,然后反序列化为对象

1.3.5 使用clone()的手段

该方法需要在被克隆类中重新clone()方法,并且实现Cloneable接口,否则将会报错(CloneNotSupportedException)

2.连接(验证,准备,解析)

2.1验证的四步

验证阶段包括以下四个步骤:

  • 文件格式验证:验证类的字节码文件是否符合 Java 虚拟机规范的文件格式。
  • 语法验证:验证类的字节码文件是否符合 Java 语言的语法规范。
  • 类型验证:验证类的字节码文件中的类型是否正确。
  • 字节码验证:验证类的字节码文件是否包含恶意代码。

2.2 准备
  • 分配内存:为类的静态变量分配内存空间。
  • 初始化值:将类的静态变量初始化为默认值。
2.3 解析

它负责将类中的符号引用转换为直接引用。

符号引用是指类名、方法名、字段名等抽象的引用,而直接引用是指内存中的实际地址。

解析阶段包括以下几个步骤:

  • 类符号引用的解析:将类符号引用转换为类的直接引用。
  • 方法符号引用的解析:将方法符号引用转换为方法的直接引用。
  • 字段符号引用的解析:将字段符号引用转换为字段的直接引用。

3.初始化

它负责初始化类的静态变量和静态方法。

初始化阶段包括以下几个步骤:

  • 静态变量的初始化:将类的静态变量初始化为指定的值。
  • 静态方法的初始化:执行类的静态方法。

4.jvm 运行时数据区

4.1 程序计数器

4.2 本地方法栈

4.3 虚拟机栈

4.4 堆

4.4.1 young (8:1:1),old

4.4.2 GCRoot,垃圾回收算法,垃圾回收器

 GCRoot:

  • 1.虚拟机栈中引用的对象
  • 2.方法区中类静态属性引用的对象
  • 3.方法区中常量引用的对象
  • 4.Native方法中引用的对象
  • 5.活动线程中的对象

垃圾回收算法(3种): 复制(新生代的3种),整理(G1),清除(CMS)

垃圾回收器:7种, ZGC, 三色标记

CMS整个运作过程分为四个大阶段,包括:

  1. 初始标记(CMS initial mark)
  2. 并发标记(CMS concurrent mark)
  3. 重新标记(CMS remark)
  4. 并发清除(CMS concurrent sweep

CMS 和 G1 (jdk1.7之后 加入的,现在使用的主流) 的区别

三色标记

4.4.3 垃圾回收的过程, Minor GC、Major GC与Full GC

4.5 方法区(元空间)

Java 虚拟机(JVM)的元空间是在 Java 7 中加入的。在 Java 6 之前,方法区是 Java 虚拟机中的一个永久代(Permanent Generation),它是堆的一个一部分。

由于方法区的大小是固定的,因此随着应用程序的运行,方法区可能会被占满,导致垃圾回收器无法回收方法区中的对象,从而导致应用程序崩溃。

元空间中存储的内容包括:

  • 类的元数据信息,例如类名、方法名、字段名等。
  • 常量池中的数据,例如字符串常量、数字常量等。
  • 注解信息。

元空间的引入,解决了方法区被占满导致应用程序崩溃的问题,同时也提高了 Java 虚拟机的灵活性。

4.6 oom的类型

除了程序计数器 之外都会发送OOM

5.执行引擎

6.性能调优

性能调优概述

JVM监控及诊断工具-GUI

JVM监控及诊断工具之命令行篇

JVM运行时参数

分析GC日志

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值