JVM类运行机制实现原理解析
作者:zi萱 发布时间:2023-11-01 18:26:20
1.一段java程序是如何运行起来的呢?
Java源文件,通过编译器,产生.Class字节码文件,字节码文件通过Java虚拟机中的解释器,编译成特定及其上的机器码,那Java虚拟机又是怎样加载java程序并执行起来的呢?
简单来说:通过类加载器加载字节码文件,被分配到JVM的运行时数据区的字节码会被执行引擎执行。
(1)类加载器,加载.class文件
(2)运行数据区:栈区、堆区、PC寄存器、本地方法栈、方法区
(3)执行引擎:执行包在装载类方法中的指令
2. 类加载器
类的加载是指将类的.class文件读入内存,将其放在方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构,并向java程序员提供访问方法区内数据结构的接口。类加载器并不需要等到某个类被首次主动使用时再加载它,JVM允许类加载器在预料某个类将要被使用时就预先加载它。
类的生命周期
类加载过程包括:加载、验证、准备、解析、初始化
(1)加载:查找并加载类的二进制数据。
a. 通过一个类的全限定名来获取其定义的二进制字节流
b. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
c. 在Java堆中生成一个代表这个类的java.lang.Class,作为对方法区中这些数据的访问入口
(2)连接:
a. 验证:确保被加载类的正确性
b. 准备:为类的静态变量分配内存,并将其初始化为默认值
c. 解析:把类中的符号引用转换为直接引用
(3)初始化:为类的静态变量赋予正确的初始值
类加载器
启动类加载器:BootstrapClassLoader,负责加载存放在JDK\jre\lib或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库。
扩展类加载器:ExtensionClassLoader,负责加载DK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库。
引用程序类加载器:ApplicationClassLoader,负责加载用户路径ClassPath所指定的类
JVM类加载机制
全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托:先让父类加载器驶入加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的类都会被缓存,当需要使用某个类时,先从缓存区寻找该Class,只有缓存区不存在该类时,才会去加载此类。
双亲委派机制
意义:系统类防止内存出出现多分同样的字节码;保证Java程序安全稳定运行
(1)当AppClassLoader去加载一个class时,它首先不会自己去加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
(2)当ExtClassLoader去加载一个class时,它首先也不会自己去加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
(3)如果BootStrapClassLoader加载失败,会使用ExtClassLoader来尝试加载。
(4)如果ExtClassLoader加载失败,会使用AppClassLoader来加载
(5)如果AppClassLoader也加载失败,则会爆出异常ClassNotFoundException
3. 运行数据区
(1)虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着一种叫“栈帧”的东东,每个方法会创建一个栈帧,栈帧中存放局部变量表(基本数据类型和对象饮用)、操作数栈、方法出口等信息,栈的大小可以固定也可以扩展,当栈调用深度大于JVM所允许的范围,会抛出StackOverFlowError。
(2)本地方法栈:主要与虚拟机用到的native方法相关,java程序员不太关心。
(3)PC寄存器:也叫程序计数器。JVM支持多线程运行,每个线程都有自己的程序计数器。若当前执行的是JVM的方法,则该寄存器中保存当前执行指令的地址,若执行native方法,则为空。
(4)堆:堆内存是JVM所有线程共享的部分,虚拟机启动时就已经创建。所有对象和数组都在堆上进行分配。这部分空间可通过GC进行回收,当申请不到空间时会抛出OutOfMemoryError。
(5)方法区:所有线程共享。主要用于存储类的信息,常量池,方法数据,方法代码等。
4. 执行引擎
方法调用会导致栈帧的入栈,会确定调用哪一个方法。
(1)栈帧。程序的执行对应着栈帧的入栈和出栈,栈帧主要包括:局部变量表、操作数栈、动态连接、方法返回地址等。
(2)方法调用。
解析调用:类加载的解析阶段,会将其中一部分符号引用转化为直接引用,这种解析的前提是方法在程序真正运行之前就有一个可确定的调用版本。编译期可确定调用方法的版本:静态方法、私有方法、实例构造器、父类方法。
分派调用:
a. 静态分派:发生在编译阶段。所有依赖于静态类型来定位方法执行版本的分派动作成为静态分派,典型方法是重载。javac编译器根据参数的静态类型决定使用哪个重载版本。
b. 动态分派:运行期根据实际类型确定方法执行版本。与方法重写有密切关系。
c. 单分派和多分派:单分派是根据一个宗量对目标方法进行选择,多分派是根据多于一个宗量对目标方法进行选择。
(3)执行引擎需将字节码转换成可以直接被JVM执行的语言,可通过以下两种方式转换:
a. 解释器:一条一条的读取,解释并且执行字节码指令
b. 即时编译器:执行引擎首先按照解释执行的方式来执行,在合适的时候,即时编译器把整段字节码编译成本地代码。内置了JIT编译器的JVM都会检查方法的执行频率,如果一个方法的执行频率超过一个特定的值的话,那么这个方法就会被编译成本地代码。
来源:https://www.jianshu.com/p/ed55a79c072d
猜你喜欢
- Spring Boot怎么实现热部署在Spring Boot实现代码热部署是一件很简单的事情,代码的修改可以自动部署并重新热启动项目。1、引
- maven scope provided和runtime例子maven常用的scope有compile,provided,runtime,t
- 目前大多数开发者使用EventBus或者Otto作为事件总线通信库,对于RxJava使用者来说,RxJava也可以轻松实现事件总线,因为它们
- 这篇文章主要介绍了java泛型常用通配符实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以
- java 代码块与静态代码块加载顺序public abstract class ClassLoadingTest {public stati
- 目录一、有效的线程1. 如何使用后台线程以避免前台负载过荷?2.如何避免应用不响应ANR?3. 如何在分离的线程中初始化查询?4.其他二、设
- malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的
- 使用Zenject和UniRx的入门级技术实现了伪登录注册功能。运行效果登录面板using System;using UniRx;using
- instanceof判断某个对象是否是某个类的实例或者某个类的子类的实例。它的判断方式大概是这样的:public<T> bool
- 最近有个需求,我需要获取所有同一类型的定时任务的对象,并自动执行。我想的方案是:直接获取某个接口下面所有的实现类的对象集合,方便以后只需要
- 为了能正常输出XML格式的内容,必须要对不被XML允许的那些特殊字符进行转换。本文介绍的正是如何使用C#判断XML字符串是否含特殊字符并进行
- 建造者模式是Java中一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程分解为多个简单对象的构建过程,并且使这些构建过程按照一定的顺
- 一、项目简述(+需求文档+PPT)功能:卡管理,卡消费,卡充值,图书借阅,消费,记录,注销等等功能。二、项目运行环境配置:Jdk1.8 +
- 在项目中有使用到延时队列的场景,做个简单的记录说明;首先DelayQueue实现了BlockingQueue,加入其中的元素必须实现Dela
- 前言:微信公众号提供了用户和用户组的管理,我们可以在微信公众号官方里面进行操作,添加备注和标签,以及移动用户组别,同时,微信公众号提供了相应
- 背景朋友想从XX超市app购买一些物美价廉的东西,但是因为人多货少经常都是缺货的状态,订阅了到货通知也没什么效果,每次收到短信通知进入app
- 前言最近接手的项目里涉及到了 GIF 动图的播放与监听,在上一版本中对于 GIF 的处理是由 H5 来实现的,因为考虑到用户体验,因此现在的
- 最近在研究springboot实现FastJson解析json数据的方法,那么今天也算个学习笔记吧!添加jar包:<dependenc
- 生命周期速览优先级servlet 的声明周期由 tomcat 服务器自行管辖,程序员无法插手;只要没有通过 url 访问 servlet,那
- 本文实例为大家分享了C#实现文件上传与下载的具体代码,供大家参考,具体内容如下C#实现文件上传代码: public ActionResult