浅谈JVM-图解类加载机制

一、目录

二、类加载机制流程

1、什么是类加载机制?

  JVM把class文件加载到内存里面,并对数据进行校验、准备、解析和初始化,最终能够被形成被JVM可以直接使用的Java类型的过程。

2、类加载流程图

3、加载

  1. 将class文件加载在内存中。
  2. 将静态数据结构(数据存在于class文件的结构)转化成方法区中运行时的数据结构(数据存在于JVM时的数据结构)。
  3. 在堆中生成一个代表这个类的java.lang.Class对象,作为数据访问的入口。

4、链接

 链接就是将Java类的二进制代码合并到java的运行状态中的过程。

  • 验证:确保加载的类符合JVM规范与安全。
  • 准备:为static变量在方法区中分配空间,设置变量的初始值。例如static int a=3,在此阶段会a被初始化为0,其他数据类型参考成员变量声明。
  • 解析:虚拟机将常量池的符号引用转变成直接引用。例如"aaa"为常量池的一个值,直接把"aaa"替换成存在于内存中的地址。
    • 符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用 的目标并不一定已经加载到内存中。
    • 直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,如果有了直接引用,那么引用的目标必定已经在内存中存在。

5、初始化

  初始化阶段是执行类构造器<clinit>()方法。在类构造器方法中,它将由编译器自动收集类中的所有类变量的赋值动作(准备阶段的a正是被赋值a)和静态变量与静态语句块static{}合并,初始化时机后续再聊。

6、使用

  正常使用。

7、卸载

  GC把无用对象从内存中卸载。

三、类加载与初始化时机

1、类加载时机

  当应用程序启动的时候,所有的类会被一次性加载吗?估计你早已知道答案,当然不能,因为如果一次性加载,内存资源有限,可能会影响应用程序的正常运行。那类什么时候被加载呢?例如,A a=new A(),一个类真正被加载的时机是在创建对象的时候,才会去执行以上过程,加载类。当我们测试的时候,最先加载拥有main方法的主线程所在类。

2、类初始化时机

 主动引用(发生类初始化过程)

  1. new一个对象。
  2. 调用类的静态成员(除了final常量)和静态方法。
  3. 通过反射对类进行调用。
  4. 虚拟机启动,main方法所在类被提前初始化。
  5. 初始化一个类,如果其父类没有初始化,则先初始化父类。

 被动引用(不会发生类的初始化)

  1. 当访问一个静态变量时,只有真正声明这个变量的类才会初始化。(子类调用父类的静态变量,只有父类初始化,子类不初始化)。
  2. 通过数组定义类引用,不会触发此类的初始化。
  3. final变量不会触发此类的初始化,因为在编译阶段就存储在常量池中。

四、图解分析类加载

 1 public class ClassLoaderProduce {
 2     static int d=3;
 3     static{
 4         System.out.println("我是ClassLoaderProduce类");
 5     }
 6     public static void main(String [] args){
 7         int b=0;
 8         String c="hello";
 9         SimpleClass simpleClass=new SimpleClass();
10         simpleClass.run();
11     }
12 }
13 
14 class SimpleClass{
15     static int  a=3;
16     static{
17         a=100;
18         System.out.println(a);
19     }
20 
21     public SimpleClass(){
22         System.out.println("对类进行加载!");
23     }
24 
25     public void run(){
26         System.out.println("我要跑跑跑!");
27     }
28 }

步骤一:装载ClassLoaderProduce类,在方法区生成动态数据结构(静态变量、静态方法、常量池、类代码),并且在堆中生成java.lang.Class对象;然后进行链接

步骤二:初始化:把static{}与静态变量合并存放在类构造器当中,对静态变量赋值。 1-5行执行完毕。

步骤三:执行main方法,首先在栈里面生成一个main方法的栈祯,定义变量b、c,注意此处的变量b、c存储的常量池存储的变量的地址,如图所示。

步骤四:创建SimpleClass对象;跟上面步骤类似:加载-链接-初始化。然后,调用run()方法的时候,它会通过classLoader局部变量的地址寻找到类的class对象并且调用run()方法

posted @ 2017-02-16 20:10  码农皮邱  阅读(...)  评论(...编辑  收藏