并行类加载与OSGI类加载

这回来分析一下OSGI的类加载机制。

先说一下OSGI能解决什么问题吧。

记得在上家公司的时候,经常参与上线。上线一般都是增加了一些功能或者修改了一些功能,然后将所有的代码重新部署。过程中要将之前的服务关掉,而且不能让客户访问。虽然每回的夜宵都不错,但还是感觉这个过程很麻烦,很别扭。

为什么明明只修改了一部分代码,却都要重新来一遍。

OSGI架构里面,很重要的一个理念就是分模块(bundle)。如果你只是修改了一个模块,就可以只热替换这个模块,不影响其它模块。想想就很有吸引力。要实现这种功能,类加载的委派模型必须大改。像AppClassLoader --》ExtClassLoader --》BootstrapClassLoader这种固定的树形结构,明显不能扩展,不能实现需求。

OSGI的规范要求每个模块都有自己的类加载器,而模块之间的依赖关系,就形成了各个类加载器之间的委派关系。这种委派关系是动态的,是自由恋爱,而不是指腹为婚。。。。。。

当然,委派是要依据规则的。这也好理解啊,谈婚论嫁时,女方的家长肯定会问,有房吗、有车吗、有几块腹肌啊。哎,又扯远了。

当一个模块(bundle)的类加载器遇到需要加载某个类或查找某个资源的请求时,规则步骤如下:

1)如果在以java.*开头的package中,那么这个请求需要委派给父类加载器

2)如果在父类委派清单所列明的package中,还是委派给父类加载器

3)如果在import-package标记描述的package中,委派给导出这个包的bundle的类加载器

4)如果在require-bundle导入的一个或多个bundle的包中,就好安装require-bundle指定的bundle清单顺序逐一委派给对应bundle的类加载器

5 )搜索bundle内部的classpath

6)搜索每个附加的fragment bundle的classpath

7)如果在某个bundle已经声明导出的package中,或者包含在已经声明导入(import-package或require-bundle)的package中,搜索终止

8)如果在某个使用dynamicimport-package声明导入的package中,尝试在运行时动态导入这个package

9)如果可以确定找到一个合适的完成动态导入的bundle,委派给该bundle的类加载器

上面这部分完全照抄周志明的著作《深入理解OSGI》。规则里面的父类加载器、bundle等概念,读者都可以从书中找到完整的讲解,我这里就不展开了。

深入理解OSGi:Equinox原理、应用与最佳实践PDF+随书源代码可从以下链接下载:

根据这个规则,所有的bundle之间的类加载形成了错综复杂的网状结构,不再是一沉不变的单一的树状结构。

但是网状结构,会有一个致命的问题。在jdk1.6包括之前,ClassLoader的类加载方法是synchronized。

protected synchronized Class<?> loadClass(String name, boolean resolve)

我们想象一个场景:bundle A 和 bundle B 互相引用了对方的package。这样在A加载B的包时,A在自己的类加载器的loadClass方法中,会最终调用到B的类加载器的loadClass方法。也就是说,A首先锁住自己的类加载器,然后再去申请B的类加载器的锁;当B加载A的包时,正好相反。这样,在多线程下,就会产生死锁。你当然可以让所有的类加载过程在单线程里按串行的方式完成,安全是安全,但是效率太低。

由此,引出了本文的另一个主题---并行类加载。

synchronized方法锁住的是当前的对象,在这种情况下,调用loadClass方法去加载一个类的时候,锁住的是当前的类加载器,也就不能再用这个类加载器去加载别的类。效率太低,而且容易出现死锁。

于是设计jdk的大牛,对这种模式进行了改进。大牛就是大牛!!!

看看jdk1.6之后的loadClass方法:

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);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        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
                }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/617343b49ea1049d082555572049a935.html