你們知道,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去作修改。
但願你們調試愉快。謝謝你們。有幫助的話,點個推薦。