JVM运行和类加载过程

时间:2021-02-18 15:29:09

 

JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method) (该知识点引用 http://www.cnblogs.com/dingyingsi/p/3760730.html)

 

堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

 

类加载机制

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

JVM运行和类加载过程

加载:

  将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时二进制数据结构,在堆中生成一个代表这个类的java.lang.class对象,作为方法区类数据的访问入口。这个过程需要类加载器的参与。

JVM运行和类加载过程

链接:  将java类的二进制代码合并到JVM的运行状态之中的过程

  验证: 确保加载的类信息符合JVM规划,没有安全方面的问题。

  准备:正式为类变量(static变量)分配内存并设置类变量的初始值的阶段,这些内存都将在方法区中进行分配。

  解析:虚拟机常量池的符号引用替换为直接引用的过程。

初始化:

  初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。

  当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先初始化其父类

  虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步

  当访问一个Java类的静态域时,只有真正声明这个域的类才会被初始化。

例如:

  JVM运行和类加载过程

结果:

   JVM运行和类加载过程

图解过程:

    JVM运行和类加载过程

 

   示例代码:

      

 1 package com.huang.classLoader;  2 public class Demo01 {  3     static {  4         System.out.println("静态初始化Demo01");  5  }  6     public static void main(String[] args){  7         System.out.println("Demo01的main方法!");  8         A a = new A();  9  System.out.println(A.width); 10         A a2 = new A(); 11  } 12 } 13 class A extends A_Father{ 14     public static int width=100; 15     static { 16         System.out.println("静态初始化类A"); 17         width = 300; 18  } 19     public A(){ 20         System.out.println("创建A的类对象"); 21  } 22 } 23 class A_Father{ 24     static { 25         System.out.println("静态初始化A_Father"); 26  } 27 }

 

 结果:   

静态初始化Demo01   //先加载Demo01
Demo01的main方法!  //然后调用main方法
静态初始化A_Father  //初始化A的父类
静态初始化类A    //然后初始化A
创建A的类对象  
300 创建A的类对象  //不需要重新加载类A

类是否初始化:

  类的主动引用(一定会发生类的初始化)

    new 一个类的对象

    调用类的静态成员(除了final常量)和静态方法

    使用java.lang.reflect包的方法对类进行反射调用

    当虚拟机启动, java Hello ,则一定会初始化Hello类,说白了就是先启动main方法所在的类

    当初始化一个类,如果其父类没有被初始化,则会先初始化他的父类

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

    当访问一个静态域时,只有真正声明这个域的类才会被初始化

      通过子类引用父类的静态变量,不会导致子类初始化

    通过数组定义类引用,不会触发此类的初始化

    引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常亮池中了)

  例如:

package com.huang.classLoader; public class Demo01 { static { System.out.println("静态初始化Demo01"); } public static void main(String[] args) throws ClassNotFoundException { //主动引用 // new A(); // System.out.println(A.width); // Class.forName("com.huang.classLoader.A"); //被动引用 // System.out.println(A.MAX); // A[] as = new A[10];
        System.out.println(B.width);//B没有被初始化
 } } class B extends A{ static { System.out.println("静态初始化B"); } } class A extends A_Father{ public static final int MAX = 100; public static int width=100; static { System.out.println("静态初始化类A"); width = 300; } public A(){ System.out.println("创建A的类对象"); } } class A_Father{ static { System.out.println("静态初始化A_Father"); } }