1. 程式人生 > >java虛擬機器類載入過程記憶體情況底層原始碼分析及ClassLoader講解

java虛擬機器類載入過程記憶體情況底層原始碼分析及ClassLoader講解

讀書筆記加自我總結-----------------------------------------------

《瘋狂JAVAj講義》

《深入理解JAVA虛擬機器》第七章虛擬機器載入機制

《傳智播客Java底層公開課視訊》教學視訊

參考:

一、虛擬機器的類載入機制

載入  連線   初始化    ------------> 程式執行期完成

壞處:                        好處:

效能開銷                   靈活性大大增強

二、虛擬機器載入的過程如下


三、載入之後在記憶體中的樣子

假設現在有這樣一個類

public class Demo {

	public static void main(String[] args) {
		new Person();
	}

}

class Person{
	private int age;
	
	public Person()
	{
		System.out.println("this is Person Construct Method");
	}
}

class Animal{
	public static String name;
	
}

它在經過載入之後大致在記憶體中就如下:(非官方圖,個人意淫)


至於new一個類之後在堆中記憶體怎麼存放,咱們下節再細說

四、過程5初始化

自動收集1.類變數賦值,2靜態語句塊  形成<clinit>方法,各語句的順序按照在類檔案中出現的順序

虛擬機器會保證一個類的<clinit>()方法在多執行緒環境被正確的加鎖、同步。

如果多執行緒同時初始化一個類,只會一個執行緒執行<clinit>()方法,其他執行緒阻塞等待。執行的執行緒執行結束後,其它執行緒喚醒之後不會再進入<clinit>()。同一個類載入器下,一個類只會初始化一次。

五、過程1載入:載入器

過程1由一個特定的元件進行完成:類載入器

除了載入階段(圖1的階段1)使用者應用程式可以通過自定義載入器參與,剩下的動作完全由虛擬機器主導控制

也就是說類載入器的工作就是“通過一個類的全類名來獲取描述此類的二進位制位元組流”

有三類載入器:參見我的上一篇部落格:

ClassLoader是一個抽象類

public abstract class ClassLoader

JVM中除根載入器之外的所有載入器都是ClassLoader子類的例項

個人認為它大致有兩類方法

1.靜態的工具方法

比如

可以通過ClassLoader.getSystemClassLoader();獲得AppClass Loader(系統類載入器)

@CallerSensitive
    public static ClassLoader getSystemClassLoader()

getParent()獲取該載入器的父類載入器

@CallerSensitive
    public final ClassLoader getParent()


2 用於載入類的方法

我們通常通過繼承這個類來實現我們的自定義類載入器

1)public的loadClass方法,對外提供的入口,進行載入類

public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

是ClassLoader類的入口點

為什麼叫入口點:

           比如我們自己寫的OurClassLoader類

           OurClassLoader ourCL=new OurClassLoader ()

           Class<?> clazz=ourCL.loadClass("OurClass");

從而載入類並得到它的Class物件

2)剩下的基本上都是protected方法

通過覆寫這些protected方法定製我們的功能

loadClass(String name,boolean resolve),注意這個是protected方法,不是外部直接使用的(不是你想呼叫就呼叫的),我們可以覆寫這個方法

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
                }

                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;
        }
    }
它會執行如下步驟

          用findLoadedClass(String)來檢查是否已經載入類,如果已經載入則直接返回

          在父類載入器上呼叫loadClass()方法。如果父類載入器為null,則使用根類載入器來載入

          如果父類載入失敗,則呼叫findClass(String)方法查詢類

findClass(String name)

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

我們要覆寫這個方法來自定義自己的載入類的行為

在這個方法中我們應該呼叫defineClass方法來把位元組碼轉為Class物件

defineClass()

方法負責將位元組碼分析成執行時的資料結構,並檢驗有效性。 此方法是final型,我們也無法重寫。

六、附加:父類委託機制

通過上面的過程我們能看到loadClass的確採用了父類委託緩衝機制

那麼問題來了:如果我們覆寫loadClass方法呢,成功避開父類委託緩衝機制(為什麼這麼任性),那能載入一個系統中已經存在的類嗎?

可以,因為:

JVM是如何判定兩個 Java 類是相同的: A.類的全名 B.載入此類的類載入器

我們自定義的一個新的類載入器,則載入這樣一個類 <com.silvia.MyClass  ,    MyClassLoader>

我們知道CLASSPATH下的類是由Application classloader 系統載入器 載入的,所以如果com.silvia.MyClass放在CLASSPATH下可以被載入

這樣就可以有兩個com.silvia.MyClass類啦

但是

能不能載入java.lang.Object這種類呢

不能。JVM裡面做了相關安全認證。從而防止不可靠的甚至惡意的程式碼代替父載入器的可靠程式碼。

所以說一般來說我們應該覆寫findClass方法就好了,不要繞過父類委託機制(不要覆寫loadClass)