java中类加载的全过程及内存图分析

类加载机制

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

(1)加载

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

(2)链接 将java类的二进制代码合并到jvm的运行状态之中的过程

2.1 验证

确保加载的类信息符合jvm规范,没有安全方面的问题。

2.2 准备 

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

2.3 解析

虚拟机常量池内的符号引用替换为直接引用的过程。(比如String s ="aaa",转化为 s的地址指向“aaa”的地址)

(3)初始化

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

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

虚拟机会保证一个类的构造器方法在多线程环境中被正确加锁和同步

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

 

技术分享

类加载过程分为:类的主动引用和类的被动引用

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

--new一个类的对象

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

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

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

--当要执行某个程序时,一定先启动main方法所在的类

 

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

--当访问一个静态变量时,只有真正生命这个静态变量的类才会被初始化(通过子类引用父类的静态变量,不会导致子类初始化)

--通过数组定义类应用,不会触发此类的初始化  A[] a = new A[10];

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

 

类加载器的层次结构(树状结构)

引导类加载器(bootstrap class loader)

--他用类加载java 的核心库(String 、Integer、List。。。)在jre/lib/rt.jar路径下的内容,是用C代码来实现的,并不继承自java.lang.ClassLoader。

--加载扩展类和应用程序类加载器。并指定他们的父类加载器。

扩展类加载器(extensions class loader)

--用来加载java的扩展库(jre/ext/*.jar路径下的内容)java虚拟机的实现会自动提供一个扩展目录。该类加载器在此目录里面查找并加载java类。

 应用程序类加载器(application class loader)

--他根据java应用的类路径(classpath路径),一般来说,java应用的类都是由他来完成加载的

自定义类加载器

--开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

技术分享

 

扩展类加载器、应用程序类加载器、自定义类加载器均是由java实现,都继承java.lang.ClassLoader类。

 

类加载器的代理模式:双亲委托机制

--就是某个特定的类加载器在接收到加载类的请求后,首先将加载任务委托给父类加载器,一次追溯,直到最高的爷爷辈的,如果父类加载器可以完成类加载任务,就成功返回;只要父类加载器无法完成次加载任务时,才自己加载。

--双亲机制是为了保证java核心库的类型安全,不会出现用户自己能定义java.lang.Object类的情况。

 

双亲委托机制是代理模式的一种,并不是所有的类加载器都采用双亲委托机制,tomcat服务器类加载器也使用代理模式,所不同的是他是首先尝试自己去加载某个类,如果找不到在代理给父类加载器。

技术分享

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。