Java类加载体系

早期的java程序员可能只要懂基本语法,还有少数的项目经验就可以找到一份比较好的工作。Java和java社区的发展,更多的人了解它,深入它。现在java程序员了解一些语法我看还远远不够了,对于jvm的了解和深入是非常重要的。网民的增多,网站的刚性需求,很多网站面临高性能,高并发等等一系列的问题。没有深入jvm的java程序员是很难写出高质量高并发的代码(也许一棒子打死所有人了,但我想绝大部分是肯定的)。

 

Osgi也许你并不陌生,但是他底层的实现机制你可能没去了解过。如果你是个打破砂锅问到底的人,你肯定会想知道osgi是如何做到的。但是你没有了解jvm的类加载体系,你肯定很难理解osgi是如果做到类隔离等一系列的问题。不过想完整理解osgi还需要其他很多方面的知识,但是它基本的机制还是的了解jvm的类加载机制。

 

Java类库有些包只是定义了一个标准,具体的实现都是由具体的供应商来提供。Java与数据库连接就是一个很好的例子。Java.sql类库只是定义了java与数据库连接的标准,那么与mysql就需要msyql的驱动,oracle就需要oracle的驱动,而java.sql类库是由bootstrap classloader加载,驱动包中的类是由system classloader来加载,不同类加载器加载的类是无法相互认识,所以自然也无法正常提供功能了。jvm又是提供了什么机制让他们交互呢?如果你确实对这些问题毫无头绪的话,那么我觉得你真的要好好理解下jvm类加载体系。

 

这篇文章主要是介绍下jvm类加载的机制基础知识。关于其他相关涉及,有时间的话,我会单独写文章来介绍。

 

1 java类加载器

http://www.blogjava.net/images/blogjava_net/yangpingyu/classloader-1.jpg

1.1 Bootstrap classloader:sun jdk是用c++实现,所以在代码中你是无法获取此加载器的实例。此加载器主要负责加载$JAVA_HOME/jre/lib/rt.jar。java类中获取结果为null,这里可以用一个例子跑下证明:

public class Test {

         public static void main(String[] arg) throws Exception{

                   ClassLoader classloader = Test.class.getClassLoader();

                   System.out.println(classloader);

                   System.out.println(classloader.getParent());

                   System.out.println(classloader.getParent().getParent());

         }

}

输出结果:

sun.misc.Launcher$AppClassLoader@19821f

sun.misc.Launcher$ExtClassLoader@addbf1

null

最后输出的null就是代表bootstrap classloader。

 

1.2 Extension classloader:主要加载扩展功能的jar,$JAVA_HOME/jre/lib/ext/*.jar。

1.3 System classloader:加载claspath中的jar包。

1.4自定义 classloader:主要加载你指定的目录中的包和类。

 

2 双亲委派模型

系统运行时,我们请求加载String类,此时System Classloader自己不找classpath中的包,把请求转发给Extension Classloader,但它也不做查找,又转发给Bootstrap Classloader,但是它发现自己没有parent了。于是他在rt.jar包中找到String类并加载到jvm中提供使用。Jvm为什么要这么实现呢?其实和java安全体系有关。假设jvm不是这么实现,我们自定义一个String类,做一些破坏,那么运行jvm的机器肯定要受到损坏。具体例子:

public class String {

         public static void main(String[] args) {

                   System.out.println("hello world");

         }

}

我们运行自定义String类的时候报错了,说没有main方法,可我们定义的明明有的,嘿嘿,委派机制的缘故最后加载到的是由bootstrap classloader在rt.jar包中的String,那个类是没有main方法,因此报错了。

http://www.blogjava.net/images/blogjava_net/yangpingyu/classloader-2.jpg

3 类隔离

jvm中的类是:类加载器+包名+类名。比如:URLClassLoader1,URLClassLoader2分别加载com.test.Test的时候会加载两次,因为每个classloader中的类对于其他classloader来说是隔离的,不认识的。例子:

import java.net.URL;
import java.net.URLClassLoader;

public class CustomClassloaderTest {
    
public static void main(String[] args) throws Exception {
        URL url = 
new URL("file:/g:/");
        URLClassLoader ucl = 
new URLClassLoader(new URL[]{url});
        Class c = ucl.loadClass("Yang");
        c.newInstance();
        System.out.println(c.getClassLoader());

        URLClassLoader ucl2 = 
new URLClassLoader(new URL[]{url});
        Class c2 = ucl2.loadClass("Yang");
        c2.newInstance();
        System.out.println(c2.getClassLoader());
    }
}

大家把Yang类存在g盘下。

public class Yang {
    
static {
        System.out.println("Yang");
    }
}

运行结果:

Yang

java.net.URLClassLoader@c17164

Yang

java.net.URLClassLoader@61de33

看到每次加载Yang类的时候都输出Yang,说明Yang类被加载了两次。

如果你不确信,可以修改下代码,让同一classloader加载Yang类两次

 

import java.net.URL;
import java.net.URLClassLoader;

public class CustomClassloaderTest {
    
public static void main(String[] args) throws Exception {
        URL url = 
new URL("file:/g:/");
        URLClassLoader ucl = 
new URLClassLoader(new URL[]{url});
        Class c = ucl.loadClass("Yang");
        c.newInstance();
        System.out.println(c.getClassLoader());

        Class c2 = ucl.loadClass("Yang");
        c2.newInstance();
        System.out.println(c2.getClassLoader());
    }
}

看看输出结果:

Yang

java.net.URLClassLoader@c17164

java.net.URLClassLoader@c17164

结果中只输出了一次Yang。因此可以证明我们最开始说的类隔离。

 

4 线程上下文类加载器

我们理解了双亲委派模型,那么目前只有由下向上单向寻找类(system->extension->bootstrap)

。我们在最开始的时候说过,java.sql包中的类由bootstrap或extension classloader加载,而mysql驱动包是在classpath中由system来加载,但bootstrap中的类是无法找到system classloader中的类,此时靠线程上下文类加载器来解决。线程上下文类加载器主要就是能让jvm类加载模型具有了向下寻找的可能,bootstrap->extension->system,如果不做任何设置,线程上下文类加载器默认是system classloader。本来这里想写一个例子的,可是有点麻烦,所以下次单独写一篇关于这方面的知识。

 

posted on 2011-05-14 21:41 yangpingyu 阅读(1015) 评论(2)  编辑  收藏 所属分类: java基础

评论

# re: Java类加载体系[未登录] 2011-11-19 21:35 Alex

请问你用的是哪个jdk,我尝试的情况是,两个classloader 加载同一个class,static仍然只是被执行一次的。  回复  更多评论   

# re: Java类加载体系 2011-11-30 15:11 yangpingyu

@Alex
我在家里用的好像是jdk1.6版本。  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 
<2011年11月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

导航

统计

常用链接

留言簿

随笔分类

随笔档案

收藏夹

linux

产品交互

分析,设计,架构

安全

技术牛人

数据库

搜索

最新评论

阅读排行榜

评论排行榜