你们知道,jdk安装的目录下,通常会有个src.zip包,这个包基本对应了rt.jar这个包。rt.jar这个包里面,就放了jdk中,jdk采用java实现的那部分类库代码,好比java.lang包下面的,什么ArrayList之类的。html
如何才能调试这部分代码呢,这里的调试,是说,可以修改源代码、加注释、直接debug。java
通过一番思考和探索后,能够这样:git
解压src.zip包,由于解压后,里面有8000多个文件,比较大,咱们也不须要调试全部的代码,我就挑了这个包下面的代码:shell
上面看到,类比较多,咱们不须要那么多,只用下面这部分:apache
新建一个普通的maven工程,而后把上面的java包下面的,拷贝到本身的工程的src目录下bootstrap
由于awt、applet之类的,如今都没人用了,我也就没拷贝那部分。数组
pom.xml真的没东西,不过仍是贴一下:app
<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>
最后,工程大概就是这样的。jvm
而后,本身在test文件夹下,我建了一个HelloWorld:maven
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(); ... }
你们运行就知道了,根本走不到咱们工程里定义的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加载的。
那就和咱们的工程里的代码不要紧了,根本不加载你的。
核心其实就是变成了,让BootStrapClassLoader优先加载咱们的类,在个人知识理解里,BootStrapClassLoader默认就是加载rt.jar的东西,怎么才能加载咱们的呢?只能求助互联网了。
而后我查到了这篇文章,-Xbootclasspath
里面说,用这个参数能够改变BootStrapClassLoader的加载路径,因而我试了一下:
-Xbootclasspath/p:"F:\gitee-ckl\jdk-debug\target\classes"
idea里,就加在这里面:
而后,你们直接run的话,能够发现,已经没问题了。
由于我改了工程里的源码的:
public boolean add(E e) { System.out.println("xxxxx"); ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
因此我这边运行的时候,会打印xxxxx:
可是,若是你debug,行号应该是对不上的,因此,咱们还要这么操做一波:
idea里,在左侧的项目树立,对着module按F4,或者右键-》Open Module Settings,会打开以下窗口:
而后debug,就能够了:
而后,这个方案,原本昨晚尝试的时候,是有问题的,不知道今天为啥就能够了,你们也能够试试。
由于昨晚尝试上面方案的时候,不知道为啥,没生效;因而找出了下面的方法。
在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时候,要去查找的路径。你们也能够参考这两篇文章:
http://www.javashuo.com/article/p-muqbkbwg-dc.html
http://www.javashuo.com/article/p-vliilzaq-b.html
因此,个人最终方案就是,把咱们的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")); } }
运行方式和前面同样:
稍微拓展一点,由于我也就知道这么一点,在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]; } }
而后在另外一个方法中,会去获取那个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去作修改。
但愿你们调试愉快。谢谢你们。有帮助的话,点个推荐。