JVM-加载,链接,初始化
Java Virtual Machine 动态的加载,链接和初始化类和接口。那么,Class 二进制文件是怎样被 JVM 加载到内存中的?JVM 如何描述一个 Java 类?类或接口怎么才能让 JVM执行?类或接口又是怎么初始化的?在JVM内部Java类的是以什么形式存在?
带着这些问题,本文将讨论:
- JVM 加载
- JVM 链接
- JVM 初始化
- JVM 内部对象表示
一个 Class 文件被 JVM 读取,然后经过加载,链接和初始化这些逻辑阶段,变成可被 JVM 识别的格式,最终在内存中的体现就是一个 java.lang.Class 对象。在虚拟机规范中并没有规定这些阶段按顺序进行,HotSpot VM 有些阶段就是交叉进行的。

1. JVM 加载
加载就是从字节流中,按照 Class 文件的格式解析出类或接口,并创建相应的类或接口。类加载的时机是在解析字节码中常量池符号的时候,比如Class.forName()、ClassLoader.loadClass()、反射和JNI_FindClass都能引发类加载。当 JVM 启动时,将会使用如下几种类加载器:
- Bootstrap Class Loader
启动类加载器,它负责加载位于 <JAVA_HOME>\jre\lib 目录的 Java 核心类库(如 rt.jar),它是使用本地方法编写,不是一个 Java 类。由它加载的类,当调用 getClassLoader() 时,返回 null 。
- Extension Class Loader
扩展类加载器,它负责加载位于 <JAVA_HOME>\jre\lib\ext 目录中的 Class 文件,由 sun.misc.Launcher$ExtClassLoader 实现。
- System Class Loader
系统类加载器,也叫应用程序类加载器(Application class loader),它负责加载位于 CLASSPATH 环境变量路径上的类库。程序的默认加载器就是它,当然也可以自定义加载器。它由 sun.misc.Launcher$AppClassLoader 实现。
- User-Defined Class Loader
用户自定义类加载器。
为了避免重复加载,和保证 Java 类型体系的安全,JVM 在加载时,使用双亲委派模型(Parents Delegation Model),它表示类加载器的一种层次关系,使用组合来实现。

当一个类加载器加载一个类时,首先它不会先尝试自己加载这个类,而是把这个加载请求委派给父类加载器,每层均是如此,把这个请求逐层上传,直到顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载时,子类加载器才会自己去加载。
根据 class 文件,类加载器在内存中创建一个 java.lang.Class 对象与之对应。在运行时,类或接口的全限定名称并不是其唯一标识,而是使用名字和加载它的类加载器共同标识的。所以当使用 instanceof,equals() 判断两个类是否相同时的必要条件是,两个类由同一个类加载器加载。在 JVM 内部有一个系统字典,它记录了系统加载的所有类,类加载器等信息,为其他模块提供检索服务。
2. JVM 链接
class 文件之间通过符号引用建立关系(符号引用以字符串的形式存在),JVM 动态加载类或接口,通过动态链接把它们链接起来,实现方法调用和相互引用。链接可分为验证,准备和解析。
- 验证
用于确保 Class 文件符合 JVM 的要求,验证字节码的正确性,验证类的元数据信息。
- 准备
为类变量分配内存并赋值各类型的默认值,如果是 final static 修饰的字段,直接会被初始化为指定的值。
- 解析
把常量池中的符号引用转换为直接引用,即运行时实际内存地址,主要有类,接口,字段和方法这 4 类符号引用。
3. JVM 初始化
初始化阶段,就是执行 Java 的初始化方法,有两个方法:一个是类初始化方法 <clinit>(静态块,语句初始化);一个是实例初始化方法 <init>(构造函数),这两个方法都是由编译器自动生成的,在初始化阶段由 JVM 自身隐式调用。
JVM 会保证父类先于子类执行 <clinit> 方法,也就是说父类的静态块和变量初始化操作先于子类的执行,在多线程环境下,JVM 会保证 <clinit> 方法的线程安全。
4. JVM 内部对象表示
HotSpot 是基于 C++ 实现的,其内部使用 OOP-Klass 二分模型来描述一个Java类的信息,其中OOP(Ordinary Object Pointer, OOPS),普通对象指针,描述对象实例信息;Klass,用来描述 Java类。我们知道 多态 是面向对象的重要特点,JVM 就是通过 Klass 实现Java 对象的分发功能。

每加载一个 Java 类,JVM 会创建一个 instanceKlass 对象(或 arrayKlass)在内部描述它;每创建一个 Java 对象,JVM 内部也会创建一个 OOP 对象来表示它。(注意区别)
(1)OOP 普通对象指针
一个 Java 对象在 JVM 内部包含两个部分,instanceOop 和实例数据。其中 instanceOop 又被成为对象头,对象头包括以下两部分信息:
- Mark Word:_mark字段,存储对象运行时信息,如哈希吗,GC分代年龄,锁信息等。数据类型为 markOop,以字长为单位,64位可以使用压缩。
- 元数据指针:指向描述类型的 Klass 对象的指针,Klass包含此类的元数据(meta-data)。

(2)instanceKlass
Klass类型的对象,是访问类元数据的入口。JVM 对每个已加载的 Java 类,内部都创建一个 instanceKlass 对象,它描述了 类的元数据,类的方法,字段,类加载器,常量等,拥有一个 Java 类运行时所需的所有信息。

instanceKlass 引用了与之对应的 java.lang.Class 对象,后者是前者的 Java 镜像。在HotSpot 内部使用 klassOop 来访问 instanceKlass,VM内部会维护一个系统字典表,记录所有已经加载的类。
JVM-加载,链接,初始化的更多相关文章
- JVM加载的初始化类
首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...
- Java类的加载 链接 初始化
原文地址 Java类的加载.链接和初始化.Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象.一个Java类从字节代码到能够 ...
- 全面解析JVM加载中初始化的时机
JVM类加载过程 JVM类加载过程分为几个阶段,分别是加载.验证.准备.解析和初始化.加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初 ...
- 关于JVM加载class文件和类的初始化
关于JVM加载class文件和类的初始化 1.JVM加载Class文件的原理机制 1.1.装载 查找并加载类的二进制数据 1.2.链接 验证:确保被加载类的正确性.(安全性考虑) 准备:为类的静态变量 ...
- JVM加载class文件的原理
当Java编译器编译好.class文件之后,我们需要使用JVM来运行这个class文件.那么最开始的工作就是要把字节码从磁盘输入到内存中,这个过程我们叫做[加载 ].加载完成之后,我们就可以进行一系列 ...
- jvm 加载class文件过程
jvm 加载class文件分为装载-链接-初始化三个过程. load -------->link verify prepare resolve ---------->initial ...
- Java提高篇——JVM加载class文件的原理机制
在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从“Hello World ”开始的,然后 ...
- JVM加载类的过程,双亲委派机制中的方法
JVM加载类的过程: 1)JVM中类的整个生命周期: 加载=>验证=>准备=>解析=>初始化=>使用=>卸载 1.1.加载 类的加载阶段,主要是获取定义此类的二进 ...
- JVM加载class文件的原理机制(转)
JVM加载class文件的原理机制 1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 2.java中的 ...
- JVM学习(二)JVM加载类
一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...
随机推荐
- MySQL基于时间字段进行分区的方案总结
MySQL支持的分区类型一共有四种:RANGE,LIST,HASH,KEY.其中,RANGE又可分为原生RANGE和RANGE COLUMNS,LIST分为原生LIST和LIST COLUMNS,HA ...
- EntityFramework 外键值映射
如果在 EF OnModelCreating 中配置了实体外键映射,也就是 SQL Server 中的 ForeignKey,那么我们在添加实体的时候,主实体的主键值会自动映射到子实体的外键值,并且这 ...
- DEBIAN下中文显示
转:http://www.cppblog.com/colorful/archive/2012/05/28/176516.aspx 一.首先检查LOCALE情况 说明:DEBIAN因为基于GNU所以,对 ...
- 软件工程-构建之法 WordCount小程序 统计文件中字符串个数,单词个数,词频,行数
一.前言 在之前写过一个词频统计的C语言课设,别人说你一个大三的怎么写C语言课程,我只想说我是先学习VB,VB是我编程语言的开始,然后接触到C语言及C++:再后来我是学习C++,然后反过来学习C语言, ...
- Eclipse与Android源码中ProGuard工具的使用
由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手实践,算是有了一个基本了解.下面将自己的 ...
- C# 中的as和is小结
在读.Net 框架设计的时候,关于C#中类型转化中的部分内容,在此总结记录,分享予大家. 首先,我们声明一个简单的继承关系. Class Father { Int x; } Class Child:F ...
- 放养的小爬虫--京东定向爬虫(AJAX获取价格数据)
放养的小爬虫--京东定向爬虫(AJAX获取价格数据) 笔者声明:只用于学习交流,不用于其他途径.源代码已上传github.githu地址:https://github.com/Erma-Wang/Sp ...
- 初识Spring
Spring简介 说到Spring就得提到它的作者"Rod Johnson",2002年他编写了<Expert One-on_One java EE设计与开发>一书.在 ...
- 创建ASP.NET Core MVC应用程序(1)-添加Controller和View
创建ASP.NET Core MVC应用程序(1)-添加Controller和View 参考文档:Getting started with ASP.NET Core MVC and Visual St ...
- 自己实现简单的AOP(四)自动初始化代理对象
前面三篇随笔,已经完成了AOP的核心功能,但 代理对象的初始化还是有些麻烦,本文将解决该问题. Demo 片段如下: public class HomeController : Controller ...