V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tianzhidao28
V2EX  ›  Java

Java ClassLoader 类加载 与 SOA 后期优化

  •  
  •   tianzhidao28 · 2015-12-15 19:50:09 +08:00 · 2614 次点击
    这是一个创建于 3301 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Java ClassLoader 类加载 与 SOA 后期优化

    标签(空格分隔): jetty soa application

    现在见到的一些应用部署方式(我见到的)

    只能说是我见到的这 2 种
    - 1 . mvn clean package 打成 war 再丢到 web 容器
    - 2 . mvn clean package 打成 jar 直接运行(这是我这一年才见到的这么粗暴的方式),但也简单干脆,
    最近看 jetty 官网也逐渐感觉或许这是个不错的单应用选择吧

    jetty 官网

    Don't deploy your application in Jetty, deploy Jetty in your application!

    use jetty as a http module,not a web container
    just a module of your application


    在一个大的系统砸碎成一片片的服务的后期,就会产生一大波妹子了, 不 , 一大波的服务 s,
    一般一个服务(以 java 为例)的组成是:

    • 一些公共 jar: spring-.jar , common-*.jar , dbjar,cache**.jar 一般有个 大于 10M
    • 一些公共 rpc jar : eg:dubbo**.jar 或其他的 jar
    • 业务相关 jar,class,resources 这些 jar 都是要加载在内存里的啊;

    10 个就重复 10 遍 9 * 10M(?)内存的浪费

    当然在 内存也就那点钱,摩尔定律 完全可以解决这个问题,

    能用钱解决的问题更本就不是问题

    到此结束!!!!
    谢谢

    朴素的劳动人民的品格教育我们,贪污浪费都是犯罪.
    同时也见到有朋友公司有把公共的一部分 jar 抽离放在容器里共享的先例。

    question

    • 写在前面的话: 首先共享的前提是:大家都在运行在同一个进程里??
      一个 tomcat 或其他 web 容器也是可以多实例运行.??

    • [x] 要共享的话 jar 应该放在哪里?直接丢在 jdk 安装目录里的 lib 行不行,为啥子( 不行!!)

    • [ ] 哪些是可以抽离去共享,大家都用的就可以共享吗

    Java Class Loader 简介

    此处输入图片的描述

    Bootstrp Class Loader 加载过的 jar 是: jre/lib,jre/classses
    ```java
    /**
    * bootstrap 加载的核心内裤
    */
    URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
    for (int i = 0; i < urls.length; i++) {
    System.out.println(urls[i].toExternalForm());
    }

    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/resources.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/rt.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/sunrsasign.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jsse.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jce.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/charsets.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/jfr.jar
    file:/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/classes

    `ExtClassLoader : ` 加载的是 jre\lib\ext 目录或者,参数 java.ext.dirs 指定的目录
    
    `AppClassLoader :` 加载 classpath 指定的目录 ,我们自己写的 Java 文件一般默认就是这货加载的,
    
    `XXXXClassLoader : `自己定义加载类的 classloader------>parent = AppCLassLoader
    
    ### 类加载机制 
    > 向上委托,全部丢给你老大,你老大再丢给他的老大,当老大搞定不了了,就交给他小弟喽。。。直到你的直接领导也搞不定 就交给你去搞
    > 全盘负责, 一旦某个 classLoader 决定加载一个类的时候,这个类用过的对象的那些类也由这个 ClassLoader 负责了..你用过的对象,别人怎么用(™D,这个世界那么多别人用过的对象...)。。。你自己统统搞定吧
    
    ![此处输入图片的描述][3]
    
    ClassLoader.java
    ```java
    
    @CallerSensitive
    public final ClassLoader getParent() {
            if (parent == null)
                return null;
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkClassLoaderPermission(parent, Reflection.getCallerClass());
            }
            return parent;
    }
    
     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) {
                            // go daddy 丢给你爸爸去加载,递归下去 找你爷爷,....
                            c = parent.loadClass(name, false);
                        } else {
                            // ExtClassLoader 的 
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        c = findClass(name);
    
                        // this is the defining class loader; record the stats
                        sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                        sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                        sun.misc.PerfCounter.getFindClasses().increment();
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
    

    随手写了一段验证下 自定义的 ClassLoader;得到的 父 ClassLoader 是 ExtClassLoader ....
    ```
    FileSystemClassLoader fileSystemClassLoader = new FileSystemClassLoader("/");

    System.out.println("classpath: " + fileSystemClassLoader.getParent().toString());

    凭什么啊! 从代码上也没看出指定父 ClassLoader 的地方,我艹
    后留意到 
    `@CallerSensitive`
    public final ClassLoader getParent() 
    
    这个鬼注解,
    从 Oracle 官网找了一段话 依然没怎么看懂,,(有了解少少或看到相关人能看懂的说明,@我)
    

    Guideline 9-9 / ACCESS-9: Safely invoke standard APIs that perform tasks using the immediate caller's class loader instance
    The following static methods perform tasks using the immediate caller's class loader:

    java.lang.Class.forName
        java.lang.Package.getPackage(s)
        java.lang.Runtime.load
        java.lang.Runtime.loadLibrary
        java.lang.System.load
        java.lang.System.loadLibrary
        java.sql.DriverManager.deregisterDriver     
        java.sql.DriverManager.getConnection
        java.sql.DriverManager.getDriver(s)
        java.util.logging.Logger.getAnonymousLogger
        java.util.logging.Logger.getLogger
        java.util.ResourceBundle.getBundle
    

    Methods such as these that vary their behavior according to the immediate caller's class are considered to be caller-sensitive, and should be annotated in code with the @CallerSensitive annotation [16].

    For example, System.loadLibrary("/com/foo/MyLib.so") uses the immediate caller's class loader to find and load the specified library. (Loading libraries enables a caller to make native method invocations.) Do not invoke this method on behalf of untrusted code, since untrusted code may not have the ability to load the same library using its own class loader instance. Do not invoke any of these methods using inputs provided by untrusted code, and do not propagate objects that are returned by these methods back to untrusted code.

    不纠结这个鬼了。
    
    ### Jetty Web 容器的 ClassLoader : 
    每个 web 上下文( web 应用和 war 文件)的普通配置是有自身的 classloader ,系统的 classloader 是它的父类。这是普通的 java classloader 的层次,但是 servlet 规范提出了负责的层次要求:
    
    1. 在 WEB-INF/lib 和 WEB-INF/classes 中包含的类的加载优先级高于父 classloader 中的类。这和普通的 java2 的 classloader 加载动作相反。
    
    2. 像 java.lang.String 这样的系统类不会被 WEB-INF/lib 或者 WEB-INF/classes 中的类替代。不幸的是, servlet 规范并没有清楚的规定哪些类是系统类,也没有清楚的指出 javax 类应该作为系统类
    
    3. Server 实现类应该对 web 应用和其他 classloader 不可见。不幸的是, servelt 规范并没有规定什么是 server class ,也没有清楚的指出像 xerces parser 这样的 common libraries 应该作为实现类。
    
    
    具体的哪些 jar 放在 jetty.home/XXX/lib 下 还是其他什么地方,看诸位的实验结果了...
    
    `(全是空口无凭)`
    ---
    ![qrcoe][4]
    
    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2563 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 03:04 · PVG 11:04 · LAX 19:04 · JFK 22:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.