JVM虚拟机类加载机制的学习

前言:学习JVM是一个漫长的课程,在你学的过程中,JVM的学习过程中是枯燥的乏味的,不要想着一口吃一个胖子,需要沉下心,一步一步来。

JVM是一个令人望而却步的领域,因为它博大精深,涉及到的内容与知识点非常之多。虽然Java开发者每天都在使用JVM,但对其有所研究并且研究深入的人却少之又少。然而,JVM的重要性却又是不言而喻的。基于JVM的各种动态与静态语言生态圈已经异常繁荣了,对JVM的运行机制有一定的了解不但可以提升我们的竞争力,还可以让我们在面对问题时能够沉着应对,加速问题的解决速度;同时还能够增强我们的自信心,让我们更加游刃有余。

本人也是刚学JVM,想要把JVM学好,最起码能够应付面试和让自己对程序的理解更上一个台阶。我这次打算从jvm的以下方面进行学习:

  • JVM介绍
  • HotSpot虚拟机讲解
  • 垃圾收集方式详解
  • 垃圾收集算法详解
  • 垃圾收集器详解
  • 分代垃圾收集机制详解
  • 新生代讲解
  • 老年代讲解
  • G1收集器分析与实例
  • 常见且重要虚拟机参数示例
  • 方法区
  • 线程共享内存区
  • 根搜索算法
  • Serial收集器
  • ParNew收集器
  • 类加载机制详解
  • 类加载的双亲委托机制
  • 字节码文件生成与分析
  • 魔数
  • 常量池与方法表
  • 各种指令详解
  • 锁详解
  • 线程安全
  • 偏向锁、自旋锁与轻量级锁
  • JIT编译器
  • GC日志生成与分析
  • 虚拟机监控工具详解
  • jConsole使用方式详解
  • 何为逃逸与逃逸分析
  • 方法内联
  • 虚拟机内存模型详解

这些东西在深入理解Java虚拟机上面其实是都有用的,但是我不会按照上面罗列的来介绍。我会按照我自己的理解来完善我这个系列博客的。

1.类加载

在java的代码中,类型的加载连接初始化的过程都是在程序运行期间完成的

加载:查找并加载二进制数据

连接:

        验证:确保加载类的正确性

        准备:为类的静态变量分配内存,将其初始化为默认值

        解析:把类中的符号引用转化为直接引用

初始化:为类的静态变量赋予正确的初始值

类加载的五个阶段:

java程序对类的使用方式有两种,主动使用和被动使用

所有的java虚拟机实现必须在每个类或者接口被Java程序“首次主动使用”时才初始化他们

何为主动使用(七种方式):

1.创建类的实例

2.访问某个类或者接口的静态变量,或者对该静态变量赋值

3.调用类的静态方法

4.反射

5.初始化一个类的子类

6.java虚拟机启动被标明启动类的类

7.jdk1.7开始提供的动态语言支持(了解)

除了这七种情况,其他使用java类的方式都被看做是对类的被动使用,都不会导致类的初始化

举一个例子来分析下啥是类的主动和被动加载

下面我们可以把子类的静态变量改为静态常量

我们可以看出来,这一次并没有执行父类的静态构造,这是为什么呢???

原因:在编译阶段,把final定义的常量放到了调用这个常量的方法所在类的常量池中(这里就放在了MyTest的常量池当中)

我们下面把代码稍微的改东下,我们再看下输出的结果

原因:当一个常量的值并非编译期间能够确定的,那么其值就不会放到调用类的常量池中,这是程序在运行的时候,会导致主动使用这个常量所在的类,会导致这个类被初始化

下面我们来分析下父子类接口的加载关系:

首先我们要先把结论说出来:当一个接口被初始化的时候,并不要求他的父类完成初始化,这一点是和类是不一样的

为什么呢:这个案例其实不是很好举的,你在接口的定义的变量其实都是被final修饰的,他们都会被放到调用类的常量池里面,从而不会加载父类接口。

我们下面来分析一下准备阶段和初始化顺序的问题(面试的时候,笔试题可能会有这种类型的题,很坑哦)

我们来根据类的准备和初始化阶段来分析下这段代码的执行顺序

这一切的结果应该和很多人的想法一致,那么我们现在把,Single类里面的b的定义放在构造方法下面,我们再来分析一下这个结果和执行情况

有两种类型的类加载器
Java虚拟机自带的加载器
根类加载器(Bootstrap):该加载器没有父加载器,它负责加载虚拟机中的核心类库。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。类加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,它并没有集成java.lang.ClassLoader类。
扩展类加载器(Extension):它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jre\lib\ext子目录(扩展目录)下加载类库,如果把用户创建的jar文件放在这个目录下,也会自动由扩展类加载器加载,扩展类加载器是纯java类,是java.lang.ClassLoader的子类。
系统应用类加载器(System):也称为应用类加载器,它的父加载器为扩展类加载器,它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,他是用户自定义的类加载器的默认父加载器。系统类加载器时纯java类,是java.lang.ClassLoader的子类。
用户自定义的类加载器
java.lang.ClassLoader的子类
用户可以定制类的加载方式
根类加载器–>扩展类加载器–>系统应用类加载器–>自定义类加载器
类加载器并不需要等到某个类被“首次主动使用”时再加载它

JVM规范允许类加载器在预料某个类将要被使用时就预先加载它,如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类才报告错误(LinkageError错误),如果这个类没有被程序主动使用,那么类加载器就不会报告错误。

类加载器用来把类加载到java虚拟机中。从JDK1.2版本开始,类的加载过程采用父亲委托机制,这种机制能更好地保证Java平台的安全。在此委托机制中,除了java虚拟机自带的根类加载器以外,其余的类加载器都有且只有一个父加载器。当java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载器能加载,则有父加载器完成加载任务,否则才由加载器loader1本身加载Sample类。
 

 

由于本人工作的原因,最近java原理这个专题可能要延后一阶段才能更新,但是java原理这个章节的内容我肯定会慢慢完善的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章