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-加载,链接,初始化的更多相关文章

  1. JVM加载的初始化类

    首先Throws(抛出)几个自己学习过程中一直疑惑的问题: 1.什么是类加载?什么时候进行类加载? 2.什么是类初始化?什么时候进行类初始化? 3.什么时候会为变量分配内存? 4.什么时候会为变量赋默 ...

  2. Java类的加载 链接 初始化

    原文地址 Java类的加载.链接和初始化.Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象.一个Java类从字节代码到能够 ...

  3. 全面解析JVM加载中初始化的时机

    JVM类加载过程 JVM类加载过程分为几个阶段,分别是加载.验证.准备.解析和初始化.加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初 ...

  4. 关于JVM加载class文件和类的初始化

    关于JVM加载class文件和类的初始化 1.JVM加载Class文件的原理机制 1.1.装载 查找并加载类的二进制数据 1.2.链接 验证:确保被加载类的正确性.(安全性考虑) 准备:为类的静态变量 ...

  5. JVM加载class文件的原理

    当Java编译器编译好.class文件之后,我们需要使用JVM来运行这个class文件.那么最开始的工作就是要把字节码从磁盘输入到内存中,这个过程我们叫做[加载 ].加载完成之后,我们就可以进行一系列 ...

  6. jvm 加载class文件过程

    jvm 加载class文件分为装载-链接-初始化三个过程. load -------->link verify prepare resolve     ---------->initial ...

  7. Java提高篇——JVM加载class文件的原理机制

    在面试java工程师的时候,这道题经常被问到,故需特别注意. 1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从“Hello World ”开始的,然后 ...

  8. JVM加载类的过程,双亲委派机制中的方法

    JVM加载类的过程: 1)JVM中类的整个生命周期: 加载=>验证=>准备=>解析=>初始化=>使用=>卸载  1.1.加载 类的加载阶段,主要是获取定义此类的二进 ...

  9. JVM加载class文件的原理机制(转)

    JVM加载class文件的原理机制 1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 2.java中的 ...

  10. JVM学习(二)JVM加载类

    一.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...

随机推荐

  1. iOS开发之手势识别

    感觉有必要把iOS开发中的手势识别做一个小小的总结.在上一篇iOS开发之自定义表情键盘(组件封装与自动布局)博客中用到了一个轻击手势,就是在轻击TextView时从表情键盘回到系统键盘,在TextVi ...

  2. T-Sql(四)表关联和视图(view)

    今天讲下T-sql中用于查询的表关联和视图,我们平时做项目的时候会遇到一些复杂的查询操作,比如有班级表,学生表,现在要查询一个学生列表,要求把学生所属班级名称也查询出来,这时候简单的select查询就 ...

  3. objective-c 语法快速过(1)

    有一定 c++或者 java 基础,过一遍 oc 语法即可,都是相通的,个人认为难点是 oc 的内存管理,虽然有了 ARC,但是也需要学习下,因为有旧软件的维护. 建立在C语言的基础上,增加了一层小范 ...

  4. git diff ^M的消除

    这是由于换行符在不同的操作系统上定义的区别造成的. Windows用CR LF来定义换行,Linux用LF. CR全称是Carriage Return ,或者表示为\r, 意思是回车. LF全称是Li ...

  5. Oracle启动报错ORA-03113解决

    环境:RHEL6.4 + Oracle 11.2.0.4 步骤摘要:1.启动报错ORA-031132.查看alert日志查找原因3.根据实际情况采取合理的措施,这里我们先增加闪回区大小,把库启动起来4 ...

  6. Oracle如何导出sys用户下的系统表

    环境:Solaris 10 + Oracle 10.2.0.4 1.导出实例 2.参考内容 Reference 1.导出实例 导出sys用户下的HIST_HEAD$和WRI$_OPTSTAT_HIST ...

  7. [mysql]支持emoji(字符集问题)

    问题的根源 主要问题就是在字符集,一般解决这种问题都是靠试验.我实验了一通,得出的结论和大家分享一下(如有错误,还望指正): 数据库的字符集 数据库连接的字符集 配置方法 设置数据库的字符集为utf8 ...

  8. SQL Server 执行计划利用统计信息对数据行的预估原理以及SQL Server 2014中预估策略的改变

    前提  本文仅讨论SQL Server查询时, 对于非复合统计信息,也即每个字段的统计信息只包含当前列的数据分布的情况下, 在用多个字段进行组合查询的时候,如何根据统计信息去预估行数的. 利用不同字段 ...

  9. js构建ui的统一异常处理方案(三)

    笔者之前分析了如何实现js的责任链异常处理的方法,通过promise这个异步模型,我们能够对同步方法和异步方法的两种情况,均可以实现责任链模式.有了这些武器,我们就可以开始设计ui的统一异常处理方案了 ...

  10. Hadoop NameNode的ZKFC机制

    博客原文:hackershell 之前在准备中级课程PPT,整理了下HA的基本内容,并且感谢松哥为我们提供了HA不会切的问题,以至于之后刚好出现的NameNode宕机,能够快速解决. NameNode ...