Java类加载分析
2016-07-20 14:54
357 查看
Java类加载
一.类加载生命周期:
类加载生命周期包括加载,验证,准备,解析,初始化,使用,卸载。
二.加载:
*过程
1.获取定义此类的二进制字节流(使用类加载器)
2.把类信息存与方法区
3.在内存中(可以是Java堆或方法区)生成一个该类的java.lang.Class对象,作为方法区中该类信息的访问入口
*要点
1.非数组类由类加载器加载
2.数组类由虚拟机直接创建,如果数组类的类型是引用类型,则递归加载该类,如果是基本数据类型,则由引导类加载 器加载。
三.验证:(安全性)
1.文件格式验证:
1.验证字节流是否符合class文件格式的规范。如魔数开头,版本号等
2.这部分的验证是基于二进制字节流的验证,经过这部分验证后就加入方法区。
2.元数据验证:
1.这部分验证是根据Java语法规范进行验证。
3.字节码验证:
1.这部分同样是Java语言的验证,主要验证方法体,避免其做出危害虚拟机安全的事件。
4.符号引用的验证:
1.因为符号引用在后面会被转换为直接引用,用来调用其他类的方法等。所以符号引用的验证可以看做是对类相关 联的信息的验证。
四.准备:(类变量分配内存与初值)
为类变量在方法区分配内存并设置初始值。类变量指的是static修饰变量,对其赋的值不是程序里指定的值,而是0值。要注意的是实例变量不在这时候开辟内存,要在类的对象实例化的时候才在Java堆中分配内存。有一个特例,即如果有一个基本数据类型的static
final变量,则会在这时候赋上程序中指定的值。
五.解析:(将符号引用替换为直接引用)
1.类或接口的解析:
对于类或接口的解析又会触发其他类的加载。这里其他类的加载会先通过当前类的加载器去加载(涉及双亲机制)。
2.字段解析(字段就是类里面定义的成员变量):
要查找类ClassA里的变量A,则先会在ClassA本身搜索,如果搜索不到就在该类实现的接口中搜索(从下到
上) 索不到就在该类的父类搜索(从下到上)。如果还搜索不到,就 会抛出java.lang.NoSuchFieldError异常。CassA的 实现的接口都有相同的子段而ClassA本身没有,则在编译器会拒绝编译。
3.类方法(不只是指static方法)解析:
要查找类ClassA里的方法a(),先在ClassA里查找,找不到则到其父类中
找(从下到上),否则再在接口中找, 在接口中找到,则说明a()是一个未实现的接口方法,则抛出 java.lang.AbstractMethodError异常。如果连在接口 不到,则宣布查找失败,抛出java.lang.NoSuchMethodError异常。当然查找到后还有进行访问权限(public,private
的验证。
4.接口方法解析:
从下到上的接口进行搜索。
六.初始化:
1.类的初始化时机:(有且仅有如下5种情况需要初始化)
1遇到new,getstatic,putstatic,invokestatic这四条字节码指令时,如果该类还没加载,则进行初始化。而这四条字
现的场景为:new一个对象;读取或设置一个静态变量;调用一个类的静态方法;
2.使用放射的时候,如果类没有初始化,则触发其初始化。
3.当初始化一个类的时候,如果其父类还没初始化,则触发其父类的初始化。
4.当虚拟机启动时,主类(包含main()的类)会先进行初始化
4000
。
5.使用JDK1.7动态语言时,解析结果的方法的类需要初始化。
除了上面5中情况会触发类的初始化,其他情况都不会触发类的初始化。如:1.通过子类引用父类的静态变量时,并不 会触发子类的初始化。2.常量(static
final修饰的变量)不属于定义它的类,而是在编译阶段存入调用类的常量池,所以 调用一个类里面定义的常量不会触发该类的初始化。3.当初始化一个类的时候如果其父类还没初始化,则父类会被初始 化,当是接口不同,只有真正使用到接口的时候(如引用接口中定义的常量),才会初始化该接口
2.类初始化所做的事:
1.执行类构造器<clinit>()方法。clinit方法执行类中所有类变量(static)的赋值动作和静态语句块(static{})
静态语句块可以正常访问在其之前的静态变量,但是只能对在其之后的静态变量赋值,而不能取值。
2.虚拟机会保证子类的<clinit>( )方法执行之前其父类的该方法先执行。所以父类的静态语句块会比子类的静态语句 先执行。
3.接口虽然不能有静态语句块,但是可以定义变量。所以接口也有<clinit>()方法,与类不同的是,在执行接口的 <clinit>()方法是不需要先执行其父接口的该方法,只有在需要使用时才会调用。
4.类的<clinit>()方法在执行的时候会被加锁,一个类只能执行一次<clinit>()方法,当多个线程同时要初始化一
个类时,只有一个线程在执行该类的<clinit>()方法,其他线程阻塞,所以,当一个类的静态语句块里有很耗时的 操作,甚至是死循环时,其他线程都会长时间阻塞。
七.类的使用:只有初始化后的类才能使用。
八.类的卸载:
类在加载的时候会把类信息存放在方法区。而方法区也是有垃圾回收机制的,当一个类被回收时就是一个类的卸载 了。一个类在回收很严格,只有当一个类被评定为无用的类时才会被回收,只有同时满足一下3个条件才能算是无 用的类。(要注意,一个被被评定为无用不一定就会马上被回收)
1.该类的所以实例对象都被回收了,即Java堆里不存在该类的实例。
2.加载该类的ClassLoader已经被回收。
3.该类对应的java.lang.Class对象没有在如何地方被引用,无法在任何地方通过反射访问该类的方法。
九.类加载器:
两个类要被判为是同一个类,必须是来自同一个类文件,且是由同一个类加载器加载的。
1.双亲委派模型:
1.双亲委派,即类的加载要先由父 类加载器来加载,依次类推到启动类加载器。如果当排到当前类加载器来加载类 是无法加载,才交给其子 类加载器去加载。这样做的好处是,保证类具有优先级关系,即相同的类会被同一个类 加载器加载,如java.lang.Object类,不管在哪里加载该类,都会被启动类加载器加载,从而保证虚拟机里只有一个
java.lang.Object类,从而保证Java的基本体系。
一.类加载生命周期:
类加载生命周期包括加载,验证,准备,解析,初始化,使用,卸载。
二.加载:
*过程
1.获取定义此类的二进制字节流(使用类加载器)
2.把类信息存与方法区
3.在内存中(可以是Java堆或方法区)生成一个该类的java.lang.Class对象,作为方法区中该类信息的访问入口
*要点
1.非数组类由类加载器加载
2.数组类由虚拟机直接创建,如果数组类的类型是引用类型,则递归加载该类,如果是基本数据类型,则由引导类加载 器加载。
三.验证:(安全性)
1.文件格式验证:
1.验证字节流是否符合class文件格式的规范。如魔数开头,版本号等
2.这部分的验证是基于二进制字节流的验证,经过这部分验证后就加入方法区。
2.元数据验证:
1.这部分验证是根据Java语法规范进行验证。
3.字节码验证:
1.这部分同样是Java语言的验证,主要验证方法体,避免其做出危害虚拟机安全的事件。
4.符号引用的验证:
1.因为符号引用在后面会被转换为直接引用,用来调用其他类的方法等。所以符号引用的验证可以看做是对类相关 联的信息的验证。
四.准备:(类变量分配内存与初值)
为类变量在方法区分配内存并设置初始值。类变量指的是static修饰变量,对其赋的值不是程序里指定的值,而是0值。要注意的是实例变量不在这时候开辟内存,要在类的对象实例化的时候才在Java堆中分配内存。有一个特例,即如果有一个基本数据类型的static
final变量,则会在这时候赋上程序中指定的值。
五.解析:(将符号引用替换为直接引用)
1.类或接口的解析:
对于类或接口的解析又会触发其他类的加载。这里其他类的加载会先通过当前类的加载器去加载(涉及双亲机制)。
2.字段解析(字段就是类里面定义的成员变量):
要查找类ClassA里的变量A,则先会在ClassA本身搜索,如果搜索不到就在该类实现的接口中搜索(从下到
上) 索不到就在该类的父类搜索(从下到上)。如果还搜索不到,就 会抛出java.lang.NoSuchFieldError异常。CassA的 实现的接口都有相同的子段而ClassA本身没有,则在编译器会拒绝编译。
3.类方法(不只是指static方法)解析:
要查找类ClassA里的方法a(),先在ClassA里查找,找不到则到其父类中
找(从下到上),否则再在接口中找, 在接口中找到,则说明a()是一个未实现的接口方法,则抛出 java.lang.AbstractMethodError异常。如果连在接口 不到,则宣布查找失败,抛出java.lang.NoSuchMethodError异常。当然查找到后还有进行访问权限(public,private
的验证。
4.接口方法解析:
从下到上的接口进行搜索。
六.初始化:
1.类的初始化时机:(有且仅有如下5种情况需要初始化)
1遇到new,getstatic,putstatic,invokestatic这四条字节码指令时,如果该类还没加载,则进行初始化。而这四条字
现的场景为:new一个对象;读取或设置一个静态变量;调用一个类的静态方法;
2.使用放射的时候,如果类没有初始化,则触发其初始化。
3.当初始化一个类的时候,如果其父类还没初始化,则触发其父类的初始化。
4.当虚拟机启动时,主类(包含main()的类)会先进行初始化
4000
。
5.使用JDK1.7动态语言时,解析结果的方法的类需要初始化。
除了上面5中情况会触发类的初始化,其他情况都不会触发类的初始化。如:1.通过子类引用父类的静态变量时,并不 会触发子类的初始化。2.常量(static
final修饰的变量)不属于定义它的类,而是在编译阶段存入调用类的常量池,所以 调用一个类里面定义的常量不会触发该类的初始化。3.当初始化一个类的时候如果其父类还没初始化,则父类会被初始 化,当是接口不同,只有真正使用到接口的时候(如引用接口中定义的常量),才会初始化该接口
2.类初始化所做的事:
1.执行类构造器<clinit>()方法。clinit方法执行类中所有类变量(static)的赋值动作和静态语句块(static{})
静态语句块可以正常访问在其之前的静态变量,但是只能对在其之后的静态变量赋值,而不能取值。
2.虚拟机会保证子类的<clinit>( )方法执行之前其父类的该方法先执行。所以父类的静态语句块会比子类的静态语句 先执行。
3.接口虽然不能有静态语句块,但是可以定义变量。所以接口也有<clinit>()方法,与类不同的是,在执行接口的 <clinit>()方法是不需要先执行其父接口的该方法,只有在需要使用时才会调用。
4.类的<clinit>()方法在执行的时候会被加锁,一个类只能执行一次<clinit>()方法,当多个线程同时要初始化一
个类时,只有一个线程在执行该类的<clinit>()方法,其他线程阻塞,所以,当一个类的静态语句块里有很耗时的 操作,甚至是死循环时,其他线程都会长时间阻塞。
七.类的使用:只有初始化后的类才能使用。
八.类的卸载:
类在加载的时候会把类信息存放在方法区。而方法区也是有垃圾回收机制的,当一个类被回收时就是一个类的卸载 了。一个类在回收很严格,只有当一个类被评定为无用的类时才会被回收,只有同时满足一下3个条件才能算是无 用的类。(要注意,一个被被评定为无用不一定就会马上被回收)
1.该类的所以实例对象都被回收了,即Java堆里不存在该类的实例。
2.加载该类的ClassLoader已经被回收。
3.该类对应的java.lang.Class对象没有在如何地方被引用,无法在任何地方通过反射访问该类的方法。
九.类加载器:
两个类要被判为是同一个类,必须是来自同一个类文件,且是由同一个类加载器加载的。
1.双亲委派模型:
1.双亲委派,即类的加载要先由父 类加载器来加载,依次类推到启动类加载器。如果当排到当前类加载器来加载类 是无法加载,才交给其子 类加载器去加载。这样做的好处是,保证类具有优先级关系,即相同的类会被同一个类 加载器加载,如java.lang.Object类,不管在哪里加载该类,都会被启动类加载器加载,从而保证虚拟机里只有一个
java.lang.Object类,从而保证Java的基本体系。
相关文章推荐
- Head First Java笔记(六)
- myeclipse破解教程
- Eclipse 中查找
- 最新eclipse整合Struts2.3.29+Hibernate5.2.1+Spring4.3.1(三)Struts+Hibernate+spring篇
- Eclipse打包带mysql的java程序
- Struts2 学习笔记
- [Java 安全]对称加密算法
- Spring源码解析(一) Spring事务控制之Hibernate
- Spring中Quartz的配置
- eclipse无法创建maven项目
- 提高java code的5个工具
- JAVA 注解的几大作用及使用方法详解
- java性能分析工具
- Java动态代理入门
- java环境搭建问题及eclipse打不开的解决方式
- Java 笔记
- java servlet过滤器简解及实例
- eclipse调试的基本意义
- java内部类的作用
- SpringJDBC