您的位置 首页 java

Java虚拟机(JVM)- 类加载系统流程

常见的虚拟机

  • HotSpot: java8默认,解释器和JIT并存,自动探索热点代码用JIT编辑成机器语言,提升性能
  • JRocketit: 适用于服务器环境,只有JIT,性能较好
  • IBM J9: 定位与HotSpot接近,广泛用于IBM的各种Java产品

JVM的工作流程

编译好的字节码文件在JVM里面的加载和执行流程如下:

  • 编写好的java源代码,经过javac编译,成为字节码文件(.class)
  • 当一个java类被使用到的时候,类会被加载进JVM (按需),类的加载由 类加载子系统 完成,一个java类经过类加载子系统后,类的meta信息会被存储进JVM的 方法区 (Method Area)。类的meta信息包含:方法代码,变量名,方法名,访问权限,返回值等类加载完成后会生成一个Class对象,Class对象是存放在 内存中类依赖的类会被递归依次加载
  • 执行引擎:运行java代码

类加载子系统:

类加载子系统加载一个java类有三步,分别是:装载,链接和初始化。

装载

通过ClassLoader来读取字节码文件,读取成功后返回一个Class<?>实例;java自带的ClassLoader有以下三种:

  • BootstrapClassLoader:由C/C++实现,在JVM内部无法获取到实例,在创建JVM虚拟机的时候自动使用BootstrapClassLoader加载java核心类库 ($jre/lib下的rt.jar, resources.jar等, String, Integer, Map等java, javax, sun开头的类),加载ExtClassLoader, SystemClassLoader (这两个ClassLoader位于rt.jar中)
  • ExtClassLoader:加载java.ext.dirs或者$jre/lib/ext下的类库,自定义的jar放到$jre/lib/ext下的话也会被ExtClassLoader加载
  • AppClassLoader:加载classpath或者java.class.path下的类库,通常用来加载自定义的类库。

这三种都继承自抽象类ClassLoader,JVM对ClassLoader的分类就两种:BootstrapClassLoader和Custom ClassLoader, ExtClassLoader和AppClassLoader都属于CustomClassLoader。

为什么需要Custom ClassLoader?

  • 隔离加载类
  • 修改类的加载方式
  • 扩展加载源
  • 防止源码泄露

ClassLoader的双亲委派机制:

  • 避免类的重复加载:通过委托去parent问问,如果加载过了就不用重新加载。
  • 沙箱安全机制:避免系统核心类被破坏,比如自己写一个java.lang.String类(同包同名),JVM在加载的时候一定会只加载rt.jar里面的String类
  • 保证核心API包的访问权限:比如自己写一个java.lang.TestString类(位于java.lang包中),暗示这个类是个JVM系统类,根据委托机制,会向上传递到BootstrapClassLoader, BootstrapClassLoader中不存在向下传递,ExtClassLoader也不存在,向下传递到AppClassLoader,强制加载会报SecurityException。即便可以通过一些手段加载,由于TestString跟其他的java.lang下的类在同一个包里,也不能访问java.lang下其他包里的可见数据,因为TestString和java.lang下的类不是由同一个ClassLoader加载的,要取得java.lang包中的权限,TestString类必须和java.lang下的其他类是来自同一个运行时包(即:同一个ClassLoader, 属于同一个包的多个类的集合)。
  • 工作原理:

能不能自己写个类叫做java.lang.System或者java.lang.TestString?

  • 通常不行
  • 但是可以通过指定JVM的参数-Xbootstrappath来完全取代java核心类,需要重写所有java核心类,
  • 可以通过写CustomClassLoader的方式实现,自定义一个ClassLoader,重写其中的loadClass方法(双亲委派机制在ClassLoader.loadClass方法中实现)
  • 可以通过SPI机制:只针对接口有效

链接

链接过程分为3个阶段:

  1. 验证:确保.class文件中的字节流包含的信息符合JVM的要求(例如hotspot jvm要求文件头以 CAFA BABE 开头),确保被加载类的正确性,不会危害虚拟机自身的安全;主要有:文件格式验证,元数据验证,字节码验证,符号引用验证。
  2. 准备:为类变量(static变量,不包含final static变量)分配内存并且设置零值(即改变量类型的默认值),注意:不是代码里面赋予的初始值,而是零值;final static修饰的变量在编译的时候就分配了,准备阶段会显式初始化;这里不会为类的实例变量初始化或者分配内存;类变量分配在方法区中,而实例变量是随着对象实例一起分配到堆内存中。
  3. 解析:将常量池内的符号引用转换为直接引用的过程。事实上解析操作往往会伴随着JVM在执行完初始化之后再执行;解析主要针对类或者接口、字段、类方法、接口方法、方法类型等,对应常量池中的CONSTANT_Class_info, CONSTANT_Field_info,CONSTANT_Methodref_info等。

初始化

  • 初始化阶段就是执行静态类构造器方法 <clinit>() 的过程。
  • 此方法是javac编译器自动收集类中的所有类变量的赋值动作和静态构造函数( static { … } ) 语句合并而来。
  • 注意: <clinit>() 不是类构造器,一般类构造器在JVM下表现为 <init>()
  • <clinit>() 中的指令按照语句在源文件中出现的顺序执行。
  • 如果该类具有父类,JVM会保证父类的 <clinit>() 一定是先于子类的 <clinit>() 被执行。
  • JVM会保证多线程下 <clinit>() 里面的代码块只会被执行一次。

文章来源:智云一二三科技

文章标题:Java虚拟机(JVM)- 类加载系统流程

文章地址:https://www.zhihuclub.com/180049.shtml

关于作者: 智云科技

热门文章

网站地图