1.jvm概述和2.jvm结构-类加载

1.jvm概述

1.1虚拟机

虚拟机(Virtual Machine),是一款软件,用来执行一系列虚拟计算机指令,大体上,虚拟机可以分为系统虚拟机和程序虚拟机。

VMware 就属于系统虚拟机,它是完全对物理计算机的仿真,提供了一 个可运行完整操作系统的软件平台。程序虚拟机典型的代表就是 java 虚拟机了,它专门为 执行某个单个计算机程序而设计。在 java 虚拟机中执行的指令我们称为 java 字节码指令。

Java 虚拟机是一种执行 java 字节码文件的虚拟计算机,它拥有独立的运行机制。

Java 技术的核心就是 java 虚拟机,因为所有的 java 程序都运行在 java 虚拟机内部。

1.2jvm作用

Java 虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对

应平台上的机器码指令执行.

java中的编译器和解释器:

Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机
器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序
只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转
换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(即
扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的
解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成
字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,
解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,这就是上面提
到的Java的特点的编译与解释并存的解释。
在这里插入图片描述

特点
  • 一次编译到处运行
  • 自动内存管理
  • 自动垃圾回收机制

现在的 JVM 不仅可以执行 java 字节码文件,还可以执行其他语言编译后的字节码文件,是一

个跨语言平台
在这里插入图片描述

1.3jvm的位置

在这里插入图片描述

.java文件先被Java编译器编译成.class文件

在这里插入图片描述

1.5各个组成部分的用途

程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需

要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中

行时数据区(Runtime Data Area),而字节码文件是 jvm 的一套指令集规范,

并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎

(Execution Engine) 将字节码翻译成底层系统指令再交由 CPU 去执行,而

这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现

整个程序的功能,这就是这 4 个主要组成部分的职责与功能。

而我们通常所说的 JVM 组成指的是运行时数据区(Runtime Data Area),因

为通常需要程序员调试分析的区域就是“运行时数据区”,或者更具体的来说就

是“运行时数据区”里面的 Heap(堆)模块,那接下来我们来看运行时数据区

(Runtime Data Area)是由哪些模块组成的

1.6java代码的执行流程

在这里插入图片描述

虽然各个平台的 java虚拟机内部实现细节不尽相同,但是它们执行的字节码内容却是一样的。

JVM 主要任务就是负责将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。

JVM 使用类加载器(Class Loader)装载 class 文件。

类加载完成后,会进行字节码校验,字节码校验通过之后 JVM 解释器会把字节码翻译成机

器码交由操作系统执行。

但不是所有的代码都是解释执行,JVM 对此作了优化,比如 HotSpot 虚拟机,它本身提供

了 JIT(Just In Time)

1.7JVM架构模型

Java 编译器输入的指令流基本上是一种基于栈的指令集架构,另一种指令集架构是基于寄存

器的指令集架构.

基于栈式架构的特点

设计和实现更简单,适用于资源受限的系统.

使用零地址指令方式分配,其执行过程依赖于操作栈,指令集更小,编译器容易实现.

不需要硬件支持,可移植性好,更好实现跨平台.

基于寄存器式架构特点:

指令完全依赖于硬件,可移植性差.

性能优秀,执行更高效.

完成一项操作使用的指令更少.

所以由于跨平台的设计,Java指令集都是根据栈来设计的,不同CPU架构不同,所以不能设计

为基于寄存器的.

优点是跨平台,指令集小,编译器容易实现.

缺点是性能下降,实现同样功能需要更多的指令.

2.jvm结构-类加载

2.1类加载子系统的作用

在这里插入图片描述

classLoader 只负责 class 文件的加载,至于它是否可以运行,则由 Execution

Engine 决定。加载的类信息存放于一块称为方法区的内存空间。除了类的信息

外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量

(这部分常量信息是 class 文件中常量池部分的内存映射).

2.2类加载ClassLoader的角色

.

在.class–>JVM–>最终称为元数据模板,此过程就要有一个运输工具(类加

载器 Class Loader),扮演一个快递员的角色.

2.3类加载过程

在这里插入图片描述

2.3.1加载
  1. 通过类名(地址)获取此类的二进制字节流.

    2.将这个字节流所代表的静态存储结构转换为方法去的运行时结构.

    3.在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的

各种数据的访问入口.

2.3.2链接:
  • 验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致;

  • 准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值

不包含用 final 修饰的 static,实例变量

  • 解析:将类的二进制数据中的符号引用替换成直接引用(符号引用是用一组符

    号描述所引用的目标;直接引用是指向目标的指针).

2.3.3初始化:
类什么时候初始化?

1 )创建类的实例,也就是 new 一个对象

2)访问某个类或接口的静态变量,或者对该静态变量赋值

3)调用类的静态方法

4)反射(Class.forName(“”))

5)初始化一个类的子类(会首先初始化子类的父类)

类的初始化顺序

对 static 修饰的变量或语句块进行赋值.

如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。

如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。

顺序是:父类 static –> 子类 static –> 父类构造方法- -> 子类构造方法

2.4类加载器分类

JVM 支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)

自定义类加载器(User-Defined ClassLoader).

从概念上来讲,自定义类加载器一般指的是程序汇总由开发人员自定义的一类加

载器,但是 Java 虚拟机规范却没有这么定义 ,而是将所有派生于抽象类

ClassLoader 的类加载器都划分为自定义类加载器.

无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有 3 个:

2.4.1引导类加载器(启动类加载器 BootStrap ClassLoader))

这个类加载器使用 C/C++语言实现,嵌套在 JVM 内部.它用来加载 java 核心类

库.

并不集成于 java.lang.ClassLoader 没有父加载器.

负责加载扩展类加载器和应用类加载器,并为他们指定父类加载器.

出于安全考虑,引用类加载器只加载包名为 java,javax,sun 等开头的类.

2.4.2扩展类加载器**(Extension ClassLoader**)

上层类加载器为引用类加载器.

从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录的

jre/lib/ext 子目录(扩展目录)下加载类库.如果用户创建的 jar 放在此目录下,也

会自动由扩展类加载器加载

2.4.3 应用程序类加载器(系统类加载器 Application ClassLoader)

派生于 ClassLoader 类.

上层类加载器为扩展类加载器.

加载我们自己定义的类.

该类加载器是程序中默认的类加载器

2.5双亲委派机制

Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要该类时才会

将它的 class 文件加载到内存中生成 class 对象.而且加载某个类的 class 文件

时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委

派模式
在这里插入图片描述

工作原理:

  1. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请

求委托给父类的加载器去执行.

  1. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终

将到达顶层的启动类加载器.

  1. 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完

成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制.

如果均加载失败,就会抛出 ClassNotFoundException 异常。

双亲委派优点?

1 安全,可避免用户自己编写的类动态替换 Java 的核心类,如 java.lang.String

2 避免全限定命名的类重复加载(使用了 findLoadClass()判断当前类是否已加

载)

2.6沙箱安全机制

作用**😗*防止恶意代码污染 java 源代码

比如上面我们定义了一个类名为 String 所在包也命名为 java.lang,因为这个类

本来是属于 jdk 的,如果没有沙箱安全机制的话,这个类将会污染到系统中的

String,但是由于沙箱安全机制,所以就委托顶层的引导类加载器查找这个类,如

果没有的话就委托给扩展类加载器,再没有就委托到系统类加载器.但是由于

String 就是 jdk 的源代码,所以在引导类加载器那里就加载到了,先找到先使用,

所以就使用引导类加载器里面的 String,后面的一概不能使用,这就保证了不被

恶意代码污染

面试题:

在 jvm 中如何判断两个对象是属于同一个类?

  1. 类的全类名(地址)完全一致.

  2. 类的加载器必须相同.

2.7 类的主动使用/被动使用

JVM 规定,每个类或者接口被首次主动使用时才对齐进行初始化,有主动使用,自

然就有被动使用.

主动使用:

1.通过 new 关键字被导致类的初始化,这是大家经常使用的初始化一个类的方式,

他肯定会导致类的加载并且初始化

2.访问类的静态变量,包括读取和更新

3.访问类的静态方法

4.对某个类进行反射操作,会导致类的初始化

5.初始化子类会导致父类的的初始化

6.执行该类的 main 函数

被动使用:

其实除了上面的 6 种主动使用就是被动使用了,但还是需要知道一些情况

1:引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量

是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导

致初始化,比如:

public final static int NUMBER = 5 ; //不会导致类初始化,被动使用

public final static int RANDOM = new Random().nextInt() ; //会导致类的初

始化,主动使用

2:构造某个类的数组时不会导致该类的初始化,比如:

6.执行该类的 main 函数

被动使用:

其实除了上面的 6 种主动使用就是被动使用了,但还是需要知道一些情况

1:引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量

是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导

致初始化,比如:

public final static int NUMBER = 5 ; //不会导致类初始化,被动使用

public final static int RANDOM = new Random().nextInt() ; //会导致类的初

始化,主动使用

2:构造某个类的数组时不会导致该类的初始化,比如:

Student[] students = new Student[10] ;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值