深入理解java虚拟机-class文件加载(包括加载机制)(7)

 

类的生命周期

 

其中加载-验证-准备-初始化-卸载的先后顺序是固定的。

解析阶段有可能发生在初始化之后(为了支持运行时绑定)

虚拟机在一下场景冲必须对类进行初始化(之前肯定先进行加载-验证-准备阶段)

  1. 遇到 new 指令 getstatic,putstatic,invokstatic
  2. 对类进行反射的时候
  3. 初始化类的时候先进行初始化其父类
  4. main方法的类,虚拟机需要先进行初始化

 

运行的时候并不会加载SubClass 
子类引用父类的静态字段,并不会触发子类的加载
 class SuperClass {

	public static int value =1;
	static {
		System.out.println(" super class init");
	}
	
}

 class SubClass extends SuperClass{
	 static {
			System.out.println(" sub class init");
		}
	 
 }

public class Test{
	 public static void main(String[] args) {
		System.out.println(SubClass.value);
	}
}
//

public class ConstClass {
	public static final String constA = "A";

	static{
		
		System.out.println(" init ");
	}
	
	
}


public class Test {

	/**
	 * 并没有触发ConstClass的初始化,因为在虚拟机常量传播优化,在编译Test.class的时    
        候 就已经是当前类常量了
	 * @param args
	 * @author created by liuyujie
	 */
	public static void main(String[] args) {
		System.out.println(ConstClass.constA);
	}
}

 

加载:
 

读取二进制字节流(不管你是本地文件还是网络二进制流等)。

目的是 将二进制流读入,将静态的二进制流,转换为方法区运行时数据结构,同时在方法区中生成Class对象(对于Hotspot);

(数组不是通过类加载器创建,是通过java虚拟机直接创建的)

类加载器是用来加载类的,比如两个实例是否相等,instance of,只有在同一个类加载器加载的时候才有意义,

不同类加载器 加载的Class对象都不一样,类加载器使用的是双亲委派模型,如上图。

bootstap启动类加载器

加载的是$JAVA_HOME/lib下的类,用户自己定义的类是无法被启动类加载器加载的,

加载器会进行 比如包名,或者类名进行检查,对于有启动类加载的类我们是木办法覆盖的。

package java.lang;

public class Integer {

	public static void main(String[] args) {
		
	}
}
错误: 在类 java.lang.Integer 中找不到主方法, 请将主方法定义为:
   public static void main(String[] args)

 String property = System.getProperty("sun.boot.class.path");

可以查看启动类加载器 加载了那些类。

extension扩展类加载器

用的eclipse,要查看这个扩展类的源码 要下载openjdk,我用的jdk7

对于的地址是 http://download.java.net/openjdk/jdk7/promoted/b147/openjdk-7-fcs-src-b147-27_jun_2011.zip(参考blog https://bbs.csdn.net/topics/360024960)

把源文件导入到项目中来就可以看了  ExtClassLoader即扩展类加载器

  String s = System.getProperty("java.ext.dirs");这个是扩展类加载的一些类

public class TeExt extends ClassLoader {
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		return super.loadClass(name);
	}
	
	
	public static void main(String[] args) throws Exception {
		Class<?> loadClass = TeExt.class.getClassLoader().loadClass("vm.c7.Int");
		System.out.println(loadClass);
		Class<?> loadClass2 = TeExt.class.getClassLoader().getParent().loadClass("javax.crypto.KeyGenerator");
		System.out.println(loadClass2);
		TeExt.class.getClassLoader().getParent().loadClass("vm.c7.Int");
	}
	
}

 

用上面的代码去加载vm.c7.Int这个类

加载代码如下

  protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);//首先去查询是否java虚拟机加载过,没有执行加载
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {//除bootstrap启动类记载器外,都有父类加载器,
                        //首先尝试使用父类记载器去加载。
                        c = parent.loadClass(name, false);//递归调用,
                    } else {//启动类加载器加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {//如果父类都加载不成功,则当前加载器尝试加载
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);//如果加载不成功抛出异常

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

运行上述代码会发现报错Exception in thread "main" java.lang.ClassNotFoundException:

TeExt.class.getClassLoader().getParent().loadClass("vm.c7.Int");这行报错。

因为在初始化话ExtClassLoader的时候

去查找扩展目录下的类,然后

回去扫描JAVA_HOME/jre/lib/ext下面的jar包。

显然启动类加载器和扩展类加载器都没有vm.c7.Int这个类,所以就。。。

很明显java的类加载机制是父类优先加载,加载不了 才会到下面实现类加载,默认的加载类即appclassloader

 

 

验证

验证目的是,确保二进制字节码不会危害虚拟机,并且符合虚拟机的要求

包括 文件格式验证(如是否caffebabe开头,版本号等),元数据验证(如,除Object外所有类都有父类,final修饰的类不能被继承,抽象类的抽象方法子类是否实现等),字节码验证(验证程序语意是否合法);

准备

准备阶段是给类变量分配内存和设置初始值的阶段,是类变量,static修饰的变量

比如 static int a=1;

准备阶段会把a初始值赋值为0;

解析

解析阶段是将符号引用替换为常量的过程,

之前使用javap -verbose 查看的一些常量池等信息,这些信息都是用符号来保存的,就是一个字符串,

并且能够准确描述这个符号 代表的类型等信息。

假如类class  Student 有一个字段 是  private Adress adress;

在加载Studen类的时候会触发加载类Addres

在类Studen中adress编译成class文件后就是一个字符串,解析就是把把address解析为

类Addres(或者其子类)的符号引用(拥有Addres的类型指针(句柄),是内存中的存在的),最后还要查看Student是否

对类Addres是否有访问权限

 

初始化

即执行类构造器<clinet>方法,将所有static按照出现的先后顺序,进行赋值和执行静态代码块,

虚拟机会保证父类<clinet>方法先于子类执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值