java虚拟机(JVM)组成结构

一、JVM整体组成概述

1、整体组成部分
1.1、类加载器(ClassLoader)
1.2、运行时数据区(Runtime Data Area)
1.3、执行引擎(Execution Engine)
1.4、本地库接口(Native Interface)
2、图解

 

3、整体运行流程

 程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中的运行时数据区(Runtime Data Area) 

而字节码文件是 jvm 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由CPU 去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是这 4 个主要组成部分的职责与功能。

而我们通常所说的 JVM 组成指的是 运行时数据区(Runtime Data  Area) ,因为通常需要程序员调试分析的区域就是“运行时数据区”,或者更具体的来说就是“运行时数据区”里面的 Heap(堆)模块。

下面具体看下这几大块

二、类加载器

1、类加载子系统

类加载子系统负责从文件系统或者网络中加载class文件。classLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。  加载的类信息存放于一块称为方法区的内存空间。

 class file 存在于硬盘上,可以理解为设计师画在纸上的模板,而最终这个模板在执行的时候是要加载 JVM 当中来,根据这个模板实例化出 n 个实例,

class file 加载到 JVM 中,被称为 DNA 元数据模板. 此过程就要有一个运输工具(类加载器 Class Loader),扮演一个快递员的角色。

2、类加载过程

3、加载

(1) 通过类名(地址)获取此类的二进制字节流。

(2)将这个字节流所代表的静态存储结构转换为方法区(元空间)的运行时结构。

(3)在内存中生成一个代表这个类的java.lang.class对象,作为这个类的各种数据的访问入口。

4、 链接
4.1、验证

检验被加载的类是否有正确的内部结构,并和其他类协调一致,如:

(1)验证文件格式是否一致: class 文件在文件开头有特定的文件标识(字节码文件都以 CA FE BA BE 标识开头);主,次版本号是否在当前 java 虚拟机接收范围内;

(2)元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java 语言规范的要求,例如这个类是否有父类;是否继承浏览不允许被继承的类(final 修饰的类);

(3)....

4.2、准备

准备阶段则负责为类的静态属性分配内存(不包含用 final 修饰的 static 常量),并设置默认初始值,在编译时进行初始化。

如: public static int value = 123;value 在准备阶段后的初始值是 0,而不是 123。

4.3、解析

将类的二进制数据中的符号引用替换成直接引用(符号引用是 Class 文件的逻辑符号,直接引用指向的方法区中某一个地址)

5、初始化

初始化,为类的静态变量赋予正确的初始值,JVM 负责对类进行初始化,主要对类变量进行初始化。初始化阶段就是执行底层类构造器方法<clinit>()的过程。此方法不需要定义,是 javac 编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来的。

5.1、初始化时机:

JVM规定:每个类或者接口被首次主动使用时才对其进行初始化,包含以下场景:

(1)通过 new 关键字创建对象

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

(3)访问类的静态方法

(4)对某个类进行反射操作

(5)初始化子类会导致父类的的初始化

(6)执行该类的 main 函数

注意:除了以上几种主动使用,以下情况被动使用,不会加载类:

(1)引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导致类加载,比如:
public final static int NUMBER = 5 ; //不会导致类初始化,被动使用
public final static int RANDOM = new Random().nextInt() ; //会导致类加载

(2)构造某个类的数组时不会导致该类的初始化,比如:
 Student[] students = new Student[10] ;

5.2、类的初始化顺序

对static修饰的变量或语句块进行赋值, 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行;如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
顺序是:父类 static –> 子类 static

public class ClassInit{
    static{
        num = 20;
    }
    static int num = 10;
    public static void main (String[] args) {
        //num从准备到初始化值变化过程 num=0 -> num=20 -> num=10
        System.out.println(num);//10
    }
}
 
 
public class ClassInit{
    static int num = 10;
    static{
        num = 20;
    }
    public static void main (String[] args) {
        //num从准备到初始化值变化过程 num=0 -> num=10 -> num=20
        System.out.println(num);//20
    }
}
6、类加载器分类

站在JVM的角度看,类加载器可以分为两种:

  • 引导类加载器(启动类加载器 Bootstrap ClassLoader).
  • 其他所有类加载器,这些类加载器由 java 语言实现,独立存在于虚拟机外部,并 且全部继承自抽象类 java.lang.ClassLoader.

站在 java 开发人员的角度来看,类加载器就应当划分得更细致一些.自 JDK1.2 以来 java 一直保持者三层类加载器:

(1)引导类加载器(启动类加载器 BootStrap ClassLoader):
       这个类加载器使用 C/C++语言实现,嵌套在 JVM 内部.它用来加载 java 核心类库.并不继承于 java.lang.ClassLoader 没有父加载器.
       负责加载扩展类加载器和应用类加载器,并为他们指定父类加载器.
       出于安全考虑,引用类加载器只加载存放在<JAVA_HOME>\lib 目录,或者被-Xbootclasspath 参数锁指定的路径中存储放的类。

(2)扩展类加载器(Extension ClassLoader):
       Java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现.
       派生于 ClassLoader 类.
       从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录的jre/lib/ext 子目录(扩展目录)下加载类库.如果用户创建的 jar 放在此目录下,也会自动由扩展类加载器加载。

(3)应用程序类加载器(系统类加载器 Application ClassLoader):
       Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现.
       派生于 ClassLoader 类.
       加载我们自己定义的类,用于加载用户类路径(classpath)上所有的类.
       该类加载器是程序中默认的类加载器.
       ClassLoader 类 , 它 是 一 个 抽 象 类 , 其 后 所 有 的 类 加 载 器 都 继 承 自 ClassLoader(不包括启动类加载器)。

7、双亲委派机制

 Java 虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要该类时才会将它的 class 文件加载到内存中生成 class 对象.而且加载某个类的 class 文件时,Java 虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式

7.1、工作原理

(1)如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请 求委托给父类的加载器去执行;
(2)如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终 将到达顶层的启动类加载器;
(3)如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完 成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制,如果均加载失败,就会抛出 ClassNotFoundException 异常。

7.2、双亲委派优点:

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

(2)避免类重复加载,当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次。

7.3、如何打破双亲委派机制

 Java 虚拟机的类加载器本身可以满足加载的要求,但是也允许开发者自定义类加载器。
       在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name), findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以重写.
       重写 loadClass 方法(是实现双亲委派逻辑的地方,修改他会破坏双亲委派机制, 不推荐)
重写 findClass 方法 (推荐)
       我们可以通过自定义类加载重写方法打破双亲委派机制, 再例如 tomcat 等都有自己定义的类加载器。

三、JVM运行时数据区

1、组成概述

JVM 的运行时数据区,不同虚拟机实现可能略微有所不同,但都会遵从 Java 虚拟机规范,

Java 8 虚拟机规范规定,Java 虚拟机所管理的内存将会包括以下几个运行时数据区域:

1.1、程序计数器(Program Counter Register)

       程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

1.2、Java 虚拟机栈(Java Virtual Machine Stacks)

  描述的是 Java 方法执行的内存模型,每个方法在执行的同时都会创建一个线帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完成的过程,都对应着一个线帧在虚拟机栈中入栈到出栈的过程。

1.3、本地方法栈(Native Method Stack)

与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的。

1.4、Java 堆(Java Heap)

是 Java 虚拟机中内存最大的一块,是被所有线程共享的,在虚拟机启动时候创

建,Java 堆唯一的目的就是存放对象实例,几乎所有的对象实例都在这里分配 内存。

1.5、 方法区(Methed Area)

 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。方法区是很重要的系统资源,是硬盘和 CPU 的中间桥梁,承载着操作系统和应用程序的实时运行.
JVM 内存布局规定了 Java 在运行过程中内存申请,分配,管理的策略,保证了 JVM的高效稳定运行.不同的 JVM 对于内存的划分方式和管理机制存在着部分差异, 以最为流行的 HotSpot 虚拟机为例:

 Java 虚拟机定义了程序运行期间会使用到的运行数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁.另外一些则是与线程一一对应的. 这些与线程对应的区域会随着线程开始和结束而创建销毁,如图: 红色的为多个线程共享,灰色的为单个线程私有的,即 线程间共享:堆,方法区. 线程私有:程序计数器,栈,本地方法栈。

2、程序计数器(Program Counter Register)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Java虚拟机结构主要由类加载器、虚拟机栈、本地方法栈、堆、方法区组成。类加载器的作用是加载Java类;虚拟机栈是用于存储局部变量、操作数栈等信息;本地方法栈是用于存储本地方法的栈;堆是Java虚拟机使用的最大内存空间,用于存储对象实例;而方法区则存放类信息、常量、静态变量等数据。 ### 回答2: Java虚拟机Java Virtual Machine,JVM)是Java程序的运行环境,它在物理机器上创建一个虚拟的计算机平台,在这个平台上执行Java字节码。JVM结构包含以下几个关键组成部分: 1. 类加载器(ClassLoader):负责加载字节码文件(.class文件)并将其转换为Java类的内存表示。类加载器可以根据需要动态加载和卸载类。 2. 执行引擎(Execution Engine):负责执行Java字节码。执行引擎将字节码解释为机器指令序列或将其编译为本地代码执行,以达到提高性能的目的。 3. 运行时数据区(Runtime Data Areas):包括多个不同类型的数据区域,用于存储程序运行所需的数据。 - 方法区(Method Area):存储被加载的类信息、常量、静态变量、即时编译器编译后的代码等。 - 堆(Heap):存储Java对象实例,堆是在JVM启动时创建的,用于存放动态分配的对象。 - 栈(Stack):存储方法执行时的局部变量、操作数栈、调用信息等。每个线程都有自己的栈,用于方法的调用和返回。 - 程序计数器(Program Counter Register):记录当前线程执行的指令地址或指令索引。 4. 本地方法接口(Native Method Interface,JNI):允许Java应用程序调用使用其他语言编写的本地库中的方法。 5. 安全性引擎(Security Engine):提供安全管理和访问控制功能,确保Java程序在执行时具有必要的权限。 这些组成部分共同构成了Java虚拟机结构Java程序在JVM上运行时,通过类加载器将程序转化为内存表示,在运行时数据区执行代码,执行引擎解释和执行字节码指令,最终完成Java程序的运行。JVM结构和功能的设计有效地将Java程序的开发与底层的操作系统解耦,提供了跨平台的能力。 ### 回答3: Java虚拟机JVM)是Java程序的运行环境,它是一个软件程序,能够解释和执行Java字节码。JVM结构可以分为以下几个部分: 1. 类加载器(Class Loader):类加载器负责加载Java类文件,将其加载到内存中,并生成对应的类对象。JVM中有三个主要的类加载器:启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)和应用程序类加载器(Application Class Loader)。 2. 运行时数据区(Runtime Data Area):运行时数据区是JVM用于存储程序运行时所需数据的区域。主要包括方法区、堆、虚拟机栈、本地方法栈和程序计数器。 - 方法区(Method Area):用于存储类的结构信息、静态变量、常量等数据。 - 堆(Heap):用于存储Java对象。所有的对象实例都分配在堆中,并可以通过引用在方法区或栈中访问。 - 虚拟机栈(VM Stack):每个线程在运行时都会创建一个对应的虚拟机栈,用于存储局部变量、方法参数、返回值等。 - 本地方法栈(Native Method Stack):与虚拟机栈类似,但用于执行本地方法。 - 程序计数器(Program Counter):用于记录当前线程执行的字节码指令地址。 3. 执行引擎(Execution Engine):执行引擎负责解释和执行字节码指令,将其转换为对应的机器指令。常见的执行引擎有两种:解释器(Interpreter)和即时编译器(Just-In-Time Compiler,JIT)。 4. 本地方法接口(Native Interface):本地方法接口提供了Java代码调用本地方法的能力。本地方法接口定义了一组规范,使得Java代码可以与C、C++等底层语言进行交互。 5. 垃圾回收系统(Garbage Collection System):垃圾回收系统负责自动管理堆内存的分配和释放,回收不再使用的对象。垃圾回收系统通过标记-清除、复制算法等方式来回收内存。 通过以上的结构Java虚拟机能够提供一种平台无关的执行环境,使得Java程序在不同的操作系统和硬件平台上都能够运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值