JVM类加载机制详解
一、先看看编写出的代码的执行过程:
二、研究类加载机制的意义
从上图可以看出,类加载是java程序运行的第一步,研究类的加载有助于了解jvm执行过程,并指导开发者采取更有效的措施配合程序执行。
研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性和适应性。
三、类加载的一般过程
原理:双亲委托模式
1、寻找jre目录,寻找jvm.dll,并初始化jvm;
2、产生一个bootstrap loader(启动类加载器);
3、bootstrap loader自动加载extended loader(标准扩展类加载器),并将其父loader设为bootstrap loader。
4、bootstrap loader自动加载appclass loader(系统类加载器),并将其父loader设为extended loader。
5、最后由appclass loader加载helloworld类。
四、类加载器的特点
1、运行一个程序时,总是由appclass loader(系统类加载器)开始加载指定的类。
2、在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3、bootstrap loader(启动类加载器)是最*的类加载器了,其父加载器为null.
五、类加载器的获取
很容易,看下面例子
public class helloworld { public static void main(string[] args) { helloworld hello = new helloworld(); class c = hello.getclass(); classloader loader = c.getclassloader(); system.out.println(loader); system.out.println(loader.getparent()); system.out.println(loader.getparent().getparent()); } }
打印结果:
sun.misc.launcher$appclassloader@19821f
sun.misc.launcher$extclassloader@addbf1
null
从上面的结果可以看出,并没有获取到extclassloader的父loader,原因是bootstrap loader(启动类加载器)是用c语言实现的,找不到一个确定的返回父loader的方式,于是就返回null。
六、类的加载
类加载有三种方式:
1、命令行启动应用时候由jvm初始化加载
2、通过class.forname()方法动态加载
3、通过classloader.loadclass()方法动态加载
三种方式区别比较大,看个例子就明白了:
package zhongqiu.common.base; public class classloaddemo { static { system.out.println("classloaddemo静态初始化块执行了!"); } public static void main(string[] args) throws classnotfoundexception { classloader loader2 = classloaddemo.class.getclassloader(); system.out.println(loader2); // 使用classloader.loadclass()来加载类,不会执行初始化块 // loader2.loadclass("zhongqiu.test.test"); // 使用class.forname()来加载类,默认会执行初始化块 // class.forname("zhongqiu.test.test"); // 使用class.forname()来加载类,并指定classloader,初始化时不执行静态块 class.forname("zhongqiu.test.test", false, loader2); } }
七、自定义classloader
package zhongqiu.common.base.classload; import java.net.malformedurlexception; import java.net.url; import java.net.urlclassloader; public class myclassloader { @suppresswarnings("resource") public static void main(string[] args) throws malformedurlexception, classnotfoundexception, illegalaccessexception, instantiationexception { url url = new url("file:/d:/javaworkspace/javacommon/src/"); classloader myloader = new urlclassloader(new url[] { url }); class c = myloader.loadclass("zhongqiu.common.base.classload.test"); test t3 = (test) c.newinstance(); } }
在java.lang包里有个classloader类,classloader 的基本目标是对类的请求提供服务,按需动态装载类和资源,只有当一个类要使用(使用new 关键字来实例化一个类)的时候,类加载器才会加载这个类并初始化。一个java应用程序可以使用不同类型的类加载器。例如web application server中,servlet的加载使用开发商自定义的类加载器, java.lang.string在使用jvm系统加载器,bootstrap class loader,开发商定义的其他类则由appclassloader加载。在jvm里由类名和类加载器区别不同的java类型。因此,jvm允许我们使用不同的加载器加载相同namespace的java类,而实际上这些相同namespace的java类可以是完全不同的类。这种机制可以保证jdk自带的java.lang.string是唯一的。
八、为什么要使用这种双亲委托模式呢?
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子classloader再加载一次。
考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的string来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为string已经在启动时被加载,所以用户自定义类是无法加载一个自定义的classloader。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!