JVM(1)-类加载过程、双亲委派、Class Format

2024-05-02 09:08

本文主要是介绍JVM(1)-类加载过程、双亲委派、Class Format,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. jvm

1.1 什么是java虚拟机?

jvm全称为Java Virtual Machine。就是java 虚拟计算机。
计算机是干什么的,计算机是用来计算的。所以java 虚拟机理解为是用来计算java的,环境话说java虚拟机是用来解析java并运行java的。既然是计算机,java虚拟机也有自己的寄存器,堆,栈等。

1.2 那么我们平常说的jvm内存和实际的物理内存是什么关系呢?

  1. 物理(操作系统)内存指的就是电脑本身的内存,内存条的内存。而jvm是运行在操作系统上的,所以jvm内存是向物理内存申请的内存。
  2. 操作系统内存分为栈和堆。栈由操作系统进行管理,也由操作系统进行垃圾回收。而堆是由用户自己分配使用(比如安装种各样的软件等。)
  3. 而jvm就是在操作系统的堆中分配的内存空间。
  4. jvm虚拟机中也存在jvm的栈、堆等。
  5. 如图所示:
    在这里插入图片描述

2. java 从编译到执行流程

既然jvm是用来解析和运行java的,到底是怎么做的呢?
在这里插入图片描述

  1. 首先我们编程的java文件是存储在硬盘中的,通过javac命令,将.java文件编译成.class文件。
  2. 当我们执行的时候,ClassLoader就会将.class文件加载到jvm内存中
  3. jvm通过解释器编译器将class文件解释成2进制,进行计算执行。

2.1 jvm是解释执行还是编译执行

jvm执行class有两种方式。
通过解释器执行,每次都进行解释,然后执行。启动比较快,但是每次都需要解释,执行慢。
通过即时编译执行,编译到本地。启动时需要编译,启动比较慢,但编译完成后执行速度快。
基于这种情况,一般采用混合执行。对于一部分热数据,进行本地编译执行,其他的还是解释执行。这样既保证了启动不会很慢,也能保证一些热数据运行很快。

2.2 jvm Java Virtual Machine

  1. java是跨平台的语言,一次编译多处运行。
  2. jvm跨语言的平台。java scala kolin groovy 等语言。
    任何语言,只要能变成class,就可以在jvm上执行。
    不论是谁,只要能变成class文件,就是jvm的菜
  3. jvm 和java无关,jvm只跟class文件有关
  4. jvm规范
    java virtual machine specifications(java虚拟机规范)
    地址:
    http://docs.oracle.com/en/java/javase/13/
    jvm是虚构出来的一台计算机。有自己的内存,cpu,储存器等指令集等。
  5. jvm的实现
    jvm是一种规范,每种环境上都有自己的实现
    linux unix windows每种都有自己的实现
    oracle官方提供的实现为:Hotspot
    可用通过java -version命令查看
    通过下图观察到jvm执行是通过 mixed mode 解释执行和编译执行混合的。
    在这里插入图片描述
  6. 其他的jvm实现
    Jrockit ,曾号称世界最快的jvm。后被Oracle收购,合并于hotspot
    TaobaoVm 是hotspot深度定制版
    LiquidVm 直接针对硬件的
    azul zing 最新垃圾回收的业界标杆。号称1ms以内,后来被hotspot吸收,发明了ZGC

3. JDK JRE JVM

jvm 执行class
jre 运行时环境,除了虚拟机,还需要一些核心类库。String Object等
jdk jre+ development kit 开发的一些工具等。
在这里插入图片描述

4.Class File Format

4.1 class文件格式解读

  1. 任何文件打开都是 0 1 01 …二进制文件,二进制字节流。计算机最终执行也是按照二进制进行执行的。那么我们的class文件到底是什么样子的呢?一个class文件到底都包含什么呢。
  2. 我们都知道,我们在编写一个java文件的时候,java文件中都有什么,有类型,有属性,有方法,会继承父类,会实现接口,还可以定义常量。但是当编译成class是什么结构呢。
  3. class的结构。标准calss文件结构。
    魔术 Magic Number
    版本号 (小版本号Minor Version ,大版本号Major Version),合起来就是这个版本号。例如jdk1.8 的版本号就是52.0. jdk1.7是51.0
    常量池count Contant_Pool_Count
    常量池具体实现 Contant_Pool
    类的访问修饰符 access_flags
    当前类名 this_class
    父类名 super_class
    实现的接口数量 interface_count
    具体的接口 iinterfaces
    属性数量 fileds_count
    具体属性 filed
    方法数量 method_count()
    具体方法 mthods
    额外属性数量 attributes_count
    额外属性具体 attributes

4.2 class文件解读

  1. 数据类型:u1 u2 u4 u8。主要根据所用字节个数区分的。
    u1 表示占一个字节,u2表示占2个字节
  2. 查看16进制的class file
    sublim/notepad++
    idea插件:BinEd
  3. 有很多可以观察byteCode(字节码)的方法
    javap
    JBE
    JClassLib idea插件
  4. classfile的构成

ClassFile{
u4 Magic Number
u2 minor Version
u2 Major Version
u2 Contant_Pool_Count

}
5. 16进制解读
CA FE BA BE 这个class文件 --》magic
00 00 minor version —》.0
00 34 major version —》 52
00 10 —》constan_pool_count常量池数量
。。。。—》具体的常量池,从#1号开始

5. 类加载

class文件是如何从硬盘中进入到内存中的呢
在这里插入图片描述
class文件 -----> classload------>linking(verification Preparetion Resolution) ------> Initializing
过程:

  1. 将.class文件通过类加载器ClassLoad加载到jvm内存中。
  2. 在jvm中进行校验(verfication),准备(preparation,静态变量赋默认值),(resolution)等。
  3. 然后进行initializing初始化。(静态变量赋初始值)
  4. 当运行完毕后,被GC

当一个class文件被jvm加载后会生成两个部分:
1.class文件中的二进制结构被读取的内存空间中
2. 在class文件被存到内存中时会生成一个Class对象指向这块内存。这个Class对象不是存储在队中的,存在metaspace中。
如果对java 反射代码比较熟悉的话比较好理解。我们的类会生成一个这个类的Class对象,通过这个Class对像可以利用反射获取到这个对象的各种结构。

5.1 类加载器

class文件 需要通过类加载器 加载到内存中。
不同的类加载器:

  1. Bootstrap 最顶层的加载器,加载范围:C++,lib/rt.jar charset.jar 等核心包
    问题:Bootstap的classLoad为什么是null,是由于BootSrap是C++开发的,在java中没有一个类名对应。
  2. Extension 加载范围:加载扩展jar包 jre/lib/ext/*.jar
  3. App 加载范围:加载classpath指定内容。也是我们平常编写代码的加载器
  4. 自定义类加载器。Custom ClassLoader 自定义加载器ClassLoader,自定义加载范围

5.2 加载过程-双亲委派机制

在这里插入图片描述

  1. 双亲委派过程
    自下往上检查是否在缓存中
    自上而下委托去find并加载。
    为啥要搞双亲委派?主要是为了安全。
    如果自己搞一个java.lang.String覆盖jdk的,那么就会用我们自己的String这样就会有危险。

  2. 父加载器 父类加载器
    父加载器不是 类加载器的加载器,不是getClassLoad().getClass().getClassLoad()
    父加载器 不是 类加载器的父类加载器。不是继承关系中的父类
    父加载器是 getPanrent()

  3. 加载器的范围
    加载器的加载范围。是在Launcher源码中指定的。
    Bootstrap 最顶层的加载器,C++,lib/rt.jar charset.jar 等核心包。
    Extension 加载扩展jar包 jre/lib/ext/*.jar
    App 加载classpath指定内容

5.3. 双亲委派机制

双亲委派是可以被打破的。重写loadClass方法
双亲委派机制是在loadClass中实现的。
什么场景需要打破双亲委派呢

  1. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
  2. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
  3. 热启动,热部署(场景比较多)
    osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本),重写了loadClass方法
  4. 打破双亲委派机制实现热部署小例子
    现状:自定义classLoad类,重写findClass()方法。这种是无法打破双亲委派机制的。
    打破:自定义classLoad类,重写loadClass()方法。打破双亲委派机制。(tomcat热部署就是这么实现的)
    热部署的时候就是将整个ClassLoad干掉,重新加载一遍类。实现热部署。

5.4 自定义加载器

  1. 每个加载类都有loadClass()方法。该方法先是向上查找父类加载器是否已经加载过,如果没有加载,则开始向下委托去加载。双亲委派
  2. 自定义加载器
    实现findClass()方法。将.class文件read到内存中二进制数组,通过defineClass()方法获取这个class文件的对象,并return
  3. 加密
    可以通过对每个二进制字节进行^ 异或操作进行加密。
    在使用时,可以在通过^异或操作进行解密。 一个二进制字节通过两个次异或操作还是原值。
  4. ClassLoad加载器的Parent是怎么指定的呢
    通过ClassLoader的构造方法
    ClassLoader(ClassLoader parent)进行指定。
    默认的父类加载器是AppClassLoader。
    ClassLoader.getSystemClassLoader()系统默认ClassLoader

5.5 编译器和解释器

java是通过jvm的解释器 和 JIT(热点代码编译)编译器来执行的。
混合模式:

  1. 起始阶段采用解释执行
  2. 热点代码检测
    多次调用的方法:方法计数器:检测方法执行频率
    多次调用的循环:循环计数器:检测循环执行频率
    进行编译。编译成本地代码
  3. -Xmixed 混合模式
    -Xint 解释模式,启动快,执行慢
    -Xcomp 纯编译,启动慢,执行快

5.6 lazyloading

  1. lazyloading严格讲应该叫lazyInitalizing
    jvm规范没有规定何时进行加载,但是严格规定了什么时候必须初始化。
    注:调用final 静态属性不会初始化类。
  2. 当一个类在被使用的时候才会去加载这个类。
    lazyInitalizing5中情况:
    –new getstatic putstatic invokestatic指令,访问final变量除外
    –java.lang.reflect对类进行反射调用时
    –初始化子类的时候,父类首先初始化
    –虚拟机启动时,被执行的主类必须初始化
    –动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化

6. linking

  1. Verification 验证文件是否符合JVM规定
  2. Preparation 静态成员变量赋默认值。
  3. Resolution 就是将符号引用变成真实的地址指针,指向真正的地址
    将类、方法、属性等符号引用解析为直接引用
    换句话说,就是常量池中的各种符合引用解析为指针,偏移量的内存地址直接引用

7. initilazing

调用类初始化代码 ,给静态成员变量赋初始值
成员变量赋值,也分为两步

  1. 先new,申请分配内存空间时,成员变量赋默认值
  2. 调用构造方法,进行初始化,赋初始值。
    总结:
    load->赋默认值 ->赋初始值
    new ->申请内存-> 赋默认值->赋初始值

这篇关于JVM(1)-类加载过程、双亲委派、Class Format的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/953897

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input