分析JVM的执行子系统

目录
  • 一、Class类文件结构
    • 1.1、JVM的平台无关性
    • 1.2、Class类文件
  • 二、类的加载机制
    • 2.1、加载
    • 2.2、验证
    • 2.3、准备阶段
    • 2.4、解析阶段
    • 2.5、初始化阶段
  • 三、类加载器
    • 3.1、双亲委派模型
    • 3.2、Tomcat是怎么保证两个应用相同名称类的隔离性

一、Class类文件结构

1.1、JVM的平台无关性

与平台无关性是建立在操作系统上,虚拟机厂商提供了许多可以运行在各种不同平台的虚拟机,它们都可以载入和执行字节码,从而实现程序的一次编写,到处运行。

各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)是构成平台无关性的基石,也是语言无关性的基础。Java 虚拟机不和包括 Java 在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联,Class 文件中包含了 Java 虚拟机指令集和符号表以及若干其他辅助信息。

1.2、Class类文件

  • Class文件是一组以8位字节为基础单位的二进制流。
  • 类似于结构体的伪结构来存储数据。
  • 只有两种数据类型:无符号数和表。
  • 无符号数属于基本的数据类型,以u1、u2、u4、u8 。
  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型。

其中值得注意的一个东西,class文件中有显示编译的版本号,使用notepad++等工具打开class文件。

图中标记前四个被称为魔数(唯一作用是确定这个文件是否为一个能被虚拟机接受的 Class 文件)。

后两个标记代表class文件的版本号,其中第4第500 00字节代表着jdk的次版本号,第6第7个字节00 34代表这jdk的主版本号,Java 的版本号是从 45 开始的,JDK 1.1 之后的每个 JDK 大版本发布主版本号向上加 1 高版本的 JDK 能向下兼容以前版本的 Class 文件,但不能运行以后版本的 Class 文件,即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的 Class 文件。

34为16进制,转化为10进制就是52,所以 52-45+1 , 代表这个class文件的版本号为jdk1.8 。

当然class文件的结构详细说起来还有常量池、访问标志、父索引、接口索引、字段表集合、方法表集合、属性表集合,这些以后有时间再补上吧,概念性东西,对实际开发代码,优化代码帮助不大。

二、类的加载机制

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7 个阶段。其中验证、准备、解析 3 个部分统称为连接(Linking)。

2.1、加载

虚拟机需要完成以下 3 件事情:

1、通过一个类的全限定名来获取定义此类的二进制字节流。

2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

3、在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

2.2、验证

是连接阶段的第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。但从整体上看,验证阶段大致上会完成下面 4 个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证。

2.3、准备阶段

为类变量分配内存并设置类变量初始值(零值)的阶段。

2.4、解析阶段

是虚拟机将常量池内的符号引用替换为直接引用的过程。

2.5、初始化阶段

是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。

到了初始化阶段,才真正开始执行类中定义的 Java 程序代码在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程。

序制定的主观计划去初始化类变量和其他资源(即调用类构造器之类的)。

5种情况会对类立即进行初始化(了解即可)

1、遇到 new、getstatic、putstatic 或 invokestatic 这 4 条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这 4 条指令的最常见的Java 代码场景是:使用 new 关键字实例化对象的时候、读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。

2、使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3、当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4、当虚拟机启动时,用户需要指定一个要执行的主类(包含 main()方法的那个类),虚拟机会先初始化这个主类。

5、当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

三、类加载器

对于任意一个类,都需要由 加载它的类加载器和这个类本身一同确立其在 Java 虚拟机中的唯一性 ,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class 文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。这里所指的“相等”,包括代表类的 Class 对象的 equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。

3.1、双亲委派模型

  • 启动类加载器 : BootstrapClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被 -Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.开头的类均被 BootstrapClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
  • 扩展类加载器 : ExtensionClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载 JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.开头的类),开发者可以直接使用扩展类加载器。
  • 应用类加载器 : ApplicationClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
  • 自定义ClassLoader :在自定义 ClassLoader 的子类时候,我们常见的会有两种做法,一种是重写 loadClass 方法,另一种是重写 findClass 方法。其实这两种方法本质上差不多毕竟 loadClass 也会调用 findClass,但是从逻辑上讲我们最好不要直接修改 loadClass 的内部逻辑。建议的做法是只在 findClass 里重写自定义类的加载方法。loadClass 这个方法是实现双亲委托模型逻辑的地方,擅自修改这个方法会导致模型被破坏,容易造成问题。因此我们最好是在双亲委托模型框架内进行小范围的改动,不破坏原有的稳定结构。同时,也避免了自己重写 loadClass 方法的过程中必须写双亲委托的重复代码,从代码的复用性来看,不直接修改这个方法始终是比较好的选择。

双亲委派模型的过程:

1、当 AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

2、当 ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

3、如果 BootStrapClassLoader加载失败(例如在 $JAVA_HOME/jre/lib里未查找到该class),会使用 ExtClassLoader来尝试加载。

4、若ExtClassLoader也加载失败,则会使用 AppClassLoader来加载,如果 AppClassLoader也加载失败,则会报出异常 ClassNotFoundException。

双亲委派模型的好处:

Java类随着它的类加载器一起具备了带有优先级的层次关系,保证java程序稳定运行。

3.2、Tomcat是怎么保证两个应用相同名称类的隔离性

tomcat下可能会同时部署多个应用,那么tomcat是怎么保证多个应用相同类(比如 SysUserService 类)呢?

  • Tomcat 本身也是一个 java 项目,因此其也需要被 JDK 的类加载机制加载,也就必然存在启动类加载器、扩展类加载器和应用系统类加载器。
  • 为了保证多应用相同类的唯一性,tomcat在一定程度上打破了双亲委派模型,每启动一个项目都会创建一个唯一的WebApp类加载器,分别用来加载不同的应用,所以就保证了类的唯一性。

以上就是分析JVM的执行子系统的详细内容,更多关于JVM 执行子系统的资料请关注我们其它相关文章!

(0)

相关推荐

  • 分析JVM源码之Thread.interrupt系统级别线程打断

    目录 一.interrupt的使用特点 二.jvm层面上interrupt方法的本质 三.ParkEvent对象的本质 四.Park()对象的本质 五.利用jni实现一个可以被打断的MyThread类 六.总结 一.interrupt的使用特点 我们先看2个线程打断的示例 首先是可打断的情况: @Test public void interruptedTest() throws InterruptedException { Thread sleep = new Thread(() -> { tr

  • 分析JVM的组成结构

    目录 一.JavaSE体系 二.运行时数据区 三.程序计数器 3.1.什么是程序计数器 3.2.程序计数器有什么特点 3.3.用个例子来说明 四.虚拟机栈 4.1.局部变量表 4.2.操作数据栈 4.3.动态链接 4.4.方法出口 4.5.栈溢出 五.本地方法栈 六.方法区 七.堆 八.运行时常量池 8.1.符号引用 8.2.字面量 8.3.jvm各版本运行时常量池变化 8.4.直接内存 一.JavaSE体系 JavaSE,Java 平台标准版,为 Java EE 和 Java ME 提供了基础

  • JVM 体系结构详解

    JVM 是一种抽象的计算机,基于堆栈架构,它有自己的指令集和内存管理,是 Java 跨平台的依据,JVM解释执行字节码,或将字节码编译成本地代码执行.Java 虚拟机体系结构如下: Class File Class File 是平台无关的二进制文件,包含着能被JVM执行的字节码,其中多字节采用大端序,字符使用一种改进的UTF-8编码.Class文件精确的描述了一个类或接口的信息,其中包括: 常量池:数值和字符串字面常量,元数据如类名.方法名称.参数,以及各种符号引用 方法的字节码指令,参数个数,

  • 浅谈JVM垃圾回收有哪些常用算法

    一.前言: 垃圾回收: 在未来的JDK中可能G1会为ZGC所取代 先问自己几个问题: 什么是垃圾? 垃圾就是堆内存中(范指)没有任何指针指向的对象实体.不具有可达性. 为什么要回收垃圾? 因为我们的内存是有限的,内存长时间不清理就会导致内存溢出,OOM: 只要是程序正在跑,那么就不断生成新的对象,我们需要GC开辟新的空间分配给新的对象. 我们怎么回收垃圾? 依靠Java的自动内存回收机制,机制的优劣由算法决定: 或者说是机制的适配度由算法和应用场景共同决定. 什么时候回收垃圾? 当堆中的实体对象

  • 初步认识JVM的体系结构

    什么是JVM? JVM(Java Virtual Machine)是一个抽象的计算机,和实际的计算机一样,它具有指令集并使用不同的存储区域,它负责执行指令,还要管理数据.内存和寄存器. 看到这里,可能不懂JVM的人,已经蒙圈了.没关系,下面让我详细为大家介绍JVM的体系架构图,或许你会明白些. 简单来说,JVM就是一个虚拟计算机.我们都知道Java语言其中的一个特性就是跨平台的,而JVM就是Java程序实现跨平台的关键部分.Java编译器编译Java程序时,生成的是与平台无关的字节码(也就是.c

  • 分析JVM的执行子系统

    目录 一.Class类文件结构 1.1.JVM的平台无关性 1.2.Class类文件 二.类的加载机制 2.1.加载 2.2.验证 2.3.准备阶段 2.4.解析阶段 2.5.初始化阶段 三.类加载器 3.1.双亲委派模型 3.2.Tomcat是怎么保证两个应用相同名称类的隔离性 一.Class类文件结构 1.1.JVM的平台无关性 与平台无关性是建立在操作系统上,虚拟机厂商提供了许多可以运行在各种不同平台的虚拟机,它们都可以载入和执行字节码,从而实现程序的一次编写,到处运行. 各种不同平台的虚

  • 详细分析JVM类加载机制

    目录 前言 1. jvm 的组成 2. 类加载 1. 加载 2. 链接 3. 初始化 3. 类加载器 引导类加载器(启动类加载器) 扩展类加载器 应用程序类加载器 4. 双亲委派机制 5. 类的主动/被动使用 结语 前言 ladies and gentleman , 你们好 ,我是羡羡 , 这节我们进入jvm的学习 , 我们知道 , jvm是java虚拟机, java代码的执行与 jvm 息息相关, 接下来我们来依次介绍 , 首先这节先来介绍 jvm 中的类加载部分 1. jvm 的组成 jvm

  • 高分面试分析jvm如何实现多态

    目录 这样说,六十分 这样说,七八十分 昨天就有一个小伙伴被一道面试题虐了,我也给了他一定深度的答案.但是我觉得不够,我觉得应该让小伙伴们像我一样,答题能答出惊喜感,于是就有了这篇文章.我会从Java层面到Hotshot源码层面再到C++层面,完整分析这个问题. 这道面试题在好一些的互联网公司,尤其是一二线,问到的概率非常大,建议小伙伴们把这篇文章吃透. 这样说,六十分 多态是面向对象的三大特性之一,我个人认为,当时设计OOP机制的时候,能够想到多态的人,真特么太牛叉了. 多态理论第一次有了具体

  • 详细分析单线程JS执行问题

    大家在学习javascript的时候很多朋友在执行问题上有疑惑,小编通过本篇文章给大家详细的分析介绍了JS的执行问题,希望能够帮助到你理解. 一.介绍 随着js不断学习,你可能会慢慢的好奇,用了这么久的js,却不知道这js在浏览器怎么被执行的,很尴尬.所以,我查阅很多资料来总结JS的执行过程,也分享出来,和大家一起学习. 本篇主要讲单线程的JS 涉及的名词:JS引擎,单线程,执行栈,执行上下文(execution context) 二.JS引擎 JS引擎是浏览器的重要组成部分,主要用于读取并执行

  • 对比分析iOS延迟执行的4种方式

    最近学习了延迟执行的几种方法,分享一下: 1.performSelector(NSObject)方法  2.NSTimer方法  3.GCD方法  4.sleep(NSThread)方法 一.performSelector方法: 复制代码 代码如下: [self performSelector:@selector(delayMethod) withObject:nil afterDelay:1.0f]; 1.特点: 此方式要求必须在主线程中执行,否则无效. 是一种非阻塞的执行方式, 暂时未找到取

  • 源码分析 Laravel 重复执行同一个队列任务的原因

    前言 laravel 的队列服务对各种不同的后台队列服务提供了统一的 API.队列允许你延迟执行消耗时间的任务,比如发送一封邮件.这样可以有效的降低请求响应的时间. 发现问题 在 Laravel 中使用 Redis 处理队列任务,框架提供的功能非常强大,但是最近遇到一个问题,就是发现一个任务被多次执行,这是为什么呢? 先说原因: 因为在 Laravel 中如果一个队列(任务)执行时间大于 60 秒,就会被认为执行失败并重新加入队列中,这样就会导致重复执行同一个任务. 这个任务的逻辑就是给用户推送

  • MySQL中通过EXPLAIN如何分析SQL的执行计划详解

    前言 在MySQL中,我们可以通过EXPLAIN命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序. 下面分别对EXPLAIN命令结果的每一列进行说明: .select_type:表示SELECT的类型,常见的取值有: 类型 说明 SIMPLE 简单表,不使用表连接或子查询 PRIMARY 主查询,即外层的查询 UNION UNION中的第二个或者后面的查询语句 SUBQUERY 子查询中的第一个 .table:输出结果集的表(表别名) .t

  • SQL执行步骤的具体分析

    SQL执行步骤的具体分析 先来看执行语句的顺序 (8)select (9)distinct A (1)from Ta (3)join Tb (2)on XXX (4)where XXX (5)group by XXX (6)with {cube|roll up} (7)having XXX (10)order by XXX (11)limit XXX 接着我们看一下具体分析查询处理的各个阶段: FROM  对from子句中的左表和右表执行笛卡尔集,产生虚拟表VT1 ON  对虚拟表VT1进行on

随机推荐