绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug)

admin 5个月前 (05-03) 科技 41 0

靠山

人人知道,jdk安装的目录下,一样平常会有个src.zip包,这个包基本对应了rt.jar这个包。rt.jar这个包内里,就放了jdk中,jdk接纳java实现的那部门类库代码,好比java.lang包下面的,什么ArrayList之类的。

若何才气调试这部门代码呢,这里的调试,是说,能够修改源代码、加注释、直接debug。

步骤

经由一番思索和探索后,可以这样:

  1. 解压src.zip包,由于解压后,内里有8000多个文件,对照大,我们也不需要调试所有的代码,我就挑了这个包下面的代码:

    上面看到,类对照多,我们不需要那么多,只用下面这部门:

  2. 新建一个通俗的maven工程,然后把上面的java包下面的,拷贝到自己的工程的src目录下

    由于awt、applet之类的,现在都没人用了,我也就没拷贝那部门。

    pom.xml真的没器械,不外照样贴一下:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
        <groupId>org.learnjdk</groupId>
    
        <modelVersion>4.0.0</modelVersion>
        <packaging>jar</packaging>
        <version>4.7.0</version>
    
        <artifactId>jdk-debug</artifactId>
        <name>jdk-debug</name>
    
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <dependencies>
    
        </dependencies>
    </project>
    

    最后,工程也许就是这样的。

    然后,自己在test文件夹下,我建了一个HelloWorld:

    import java.util.ArrayList;
    
    public class HelloWorld {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            list.add("abc");
        }
    }
    

    理论上来说,就可以调试了吗?naive!

    F:\gitee-ckl\rocketmq-all-4.7.0-source>java -version
    java version "1.8.0_11"
    Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

    我这边是jdk的版本,会有个报错,是源码不完全匹配class导致的,我这边会报找不到java.lang.Iterable#iterator方式,人人直接反编译一下jdk的这个class,就能看到缺了啥了,我这边加上这个方式就好了:

    public interface Iterable<T> {
        /**
         * Returns an iterator over elements of type {@code T}.
         *
         * @return an Iterator.
         */
        public abstract Iterator<T> iterator();
        
    	...
    }
    

测试demo有什么问题

人人运行就知道了,基本走不到我们工程里界说的class

实在整体来说,谁人maven工程是没问题的。走不到谁人class,是由于,classloader的问题。

在运行上面的helloWorld时,当前classloader是sun.misc.Launcher.AppClassLoader,它的父类是

sun.misc.Launcher.ExtClassLoader,而sun.misc.Launcher.ExtClassLoader的父类,就是BootStrap类加载器了。

由于AppClassLoader是遵照双亲委派的,以是,在运行下面这个代码的时刻:

import java.util.ArrayList;

public class HelloWorld {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
    }
}

看到ArrayList,AppClassLoader会交给ExtClassLoader去加载,ExtClassLoader会交给BootStrapClassloader去加载,BootStrapClassloader自己卖力加载jdk下的rt.jar等焦点jar包,而Arraylist正好就是在jdk下面的rt.jar中,以是,最终,Arraylist是由BootStrapClassloader加载的。

那就和我们的工程里的代码没关系了,基本不加载你的。

怎么让BootStrap优先加载我们的类

焦点实在就是变成了,让BootStrapClassLoader优先加载我们的类,在我的知识明白里,BootStrapClassLoader默认就是加载rt.jar的器械,怎么才气加载我们的呢?只能求助互联网了。

然后我查到了这篇文章,-Xbootclasspath

内里说,用这个参数可以改变BootStrapClassLoader的加载路径,于是我试了一下:

-Xbootclasspath/p:"F:\gitee-ckl\jdk-debug\target\classes"

idea里,就加在这内里:

绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug) 第1张

然后,人人直接run的话,可以发现,已经没问题了。

由于我改了工程里的源码的:

    public boolean add(E e) {
        System.out.println("xxxxx");
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

以是我这边运行的时刻,会打印xxxxx:

绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug) 第2张

然则,若是你debug,行号应该是对不上的,以是,我们还要这么操作一波:

idea里,在左侧的项目树立,对着module按F4,或者右键-》Open Module Settings,会打开如下窗口:

绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug) 第3张

然后debug,就可以了:

绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug) 第4张

然后,这个方案,原本昨晚实验的时刻,是有问题的,不知道今天为啥就可以了,人人也可以试试。

另一种可行的方案

由于昨晚实验上面方案的时刻,不知道为啥,没生效;于是找出了下面的方式。

在helloWorld.java里,修改如下:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.print(System.getProperty("sun.boot.class.path"));
        
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");
    }
}

我们打印了sun.boot.class.path的值,我这边打印出来后如下:

C:\Program Files\Java\jdk1.8.0_11\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_11\jre\classes

这个参数啥意思,差不多就是BootStrap加载class时刻,要去查找的路径。人人也可以参考这两篇文章:

https://www.cnblogs.com/ahudyan-forever/p/6007458.html

https://blog.csdn.net/briblue/article/details/54973413

以是,我的最终方案就是,把我们的class路径,放到最前面,人人凭据自己的路径举行修改就行。

人人要注意的是,这里的路径,要仔细,粘错一个字符都不行:

-Dsun.boot.class.path="F:\gitee-ckl\jdk-debug\target\classes;C:\Program Files\Java\jdk1.8.0_11\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_11\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_11\jre\classes"

最好直接用下面这个代码来拼好路径,省得人工失足:

public class HelloWorld {
    public static void main(String[] args) {
        String property = System.getProperty("sun.boot.class.path");
        System.setProperty("sun.boot.class.path","F:\\gitee-ckl\\jdk-debug\\target\\classes;" + property);
        System.out.println(System.getProperty("sun.boot.class.path"));
		
    }
}

运行方式和前面一样:

绍兴气象:曹工力荐:调试 jdk 中 rt.jar 包部门的源码(可自由增添注释,修改代码并debug) 第5张

该方案为什么可行

稍微拓展一点,由于我也就知道这么一点,在sun.misc.Launcher类中:

public class Launcher {
    private static URLStreamHandlerFactory factory = new Launcher.Factory();
    private static Launcher launcher = new Launcher();
    // 1
    private static String bootClassPath = System.getProperty("sun.boot.class.path");

1处,这里界说了一个field,就是去获取我们前面用到的谁人属性。

这个类里,有另一个方式来剖析这个field。

sun.misc.Launcher.BootClassPathHolder
private static class BootClassPathHolder
  {
    // 0
    static final URLClassPath bcp = new URLClassPath(arrayOfURL, Launcher.factory);

    static
    {
      URL[] arrayOfURL;
      if (Launcher.bootClassPath != null) {
        arrayOfURL = (URL[])AccessController.doPrivileged(new PrivilegedAction()
        {
          public URL[] run() {
            // 1
            File[] arrayOfFile = Launcher.getClassPath(Launcher.bootClassPath);
            int i = arrayOfFile.length;
            HashSet localHashSet = new HashSet();
            for (int j = 0; j < i; j++) {
              // 2
              File localFile = arrayOfFile[j];

              if (!localFile.isDirectory()) {
                localFile = localFile.getParentFile();
              }
              if ((localFile != null) && (localHashSet.add(localFile))) {
                MetaIndex.registerDirectory(localFile);
              }
            }
            // 3
            return Launcher.pathToURLs(arrayOfFile);
          }
        });
      }
      else
        arrayOfURL = new URL[0];
    }
  }
  • 1处,把谁人属性,用分隔符离开,剖析为一个文件数组
  • 2处,遍历数组
  • 3处,剖析为URL,赋值给arrayOfURL。由于这里是一个匿名内部类,以是第三步的return,只是return了匿名内部类中的方式
  • 0处,使用arrayOfURL,界说了一个static变量

然后在另一个方式中,会去获取谁人bcp:

sun.misc.Launcher#getBootstrapClassPath    
public static URLClassPath getBootstrapClassPath() {
        return Launcher.BootClassPathHolder.bcp;
}

上面这个方式在哪被挪用?

java.lang.ClassLoader#getBootstrapClassPath
// Returns the URLClassPath that is used for finding system resources.
static URLClassPath getBootstrapClassPath() {
    return sun.misc.Launcher.getBootstrapClassPath();
}

被同属于ClassLoader类的下列方式挪用:

java.lang.ClassLoader#getBootstrapResource    
	/**
     * Find resources from the VM's built-in classloader.
     */
    private static URL getBootstrapResource(String name) {
        URLClassPath ucp = getBootstrapClassPath();
        Resource res = ucp.getResource(name);
        return res != null ? res.getURL() : null;
    }


再往上找,就是:

java.lang.ClassLoader#getResource    
public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

其他就不多剖析,人人也对照熟悉了。

两种方案的差异

第一种方案,加了-Xbootclasspath,这个是在jvm层面去做修改,由于这个-X是虚拟机参数;

第二种方案,上面人人也看到了,是在rt.jar中,java层面的classloader去做修改。

总结

希望人人调试愉快。谢谢人人。有辅助的话,点个推荐。

,

Sunbet 申博

Sunbet 申博www.cdbxlplvs.com Sunbet是Sunbet的官方网站。Sunbt官网有你喜欢的Sunbet、申博APP下载、Sunbet最新网址、Sunbet管理网最新网址等。

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:441
  • 页面总数:0
  • 分类总数:8
  • 标签总数:929
  • 评论总数:127
  • 浏览总数:4557