比起死板的理論,我更喜歡靈活的代碼操作,畢竟程序不是靜態(tài)的,程序是靜態(tài)的,是千變?nèi)f化的,每一行代碼,在不同時間執(zhí)行都可能產(chǎn)生不同的結(jié)果,每學(xué)一個理論,最終都需要踐踏實地的實踐,或是應(yīng)用在項目中,或是自己寫一個小demo,有了輸出,才有了更好的收獲。
今天寫一些new Object()做了哪些事
首先簡單寫一個方法
public class ObjectDemo {
public static void main(String[] args) {
Object obj = new Object();
}
}
然后找到這個類的class文件地址
執(zhí)行javac - p ObjectDemo 輸出字節(jié)碼(匯編碼)
Code:
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."":
7: astore_1
8: return
上面就是計算機(jī)要執(zhí)行的匯編碼
一 從匯編碼的角度解釋這個過程
- new 首先jvm看如果沒有對象的class那么就會去找這個class,然后進(jìn)行加載對象中屬性,并且將對象中的每一個屬性,都放到在堆中開辟的內(nèi)存空間中,并且產(chǎn)生一個指針(對象引用),并將這個指針壓入棧中。
- dup 上一步將對象引用壓入了棧,現(xiàn)在將這個引用復(fù)制一個,并且也壓入棧,這時候棧中就有了兩個對象引用,但是,他們的分工不同,一個是負(fù)責(zé)設(shè)置屬性值的,另一個是負(fù)責(zé)對象引用的
- invokespecial 這一步是用來初始化的,比如構(gòu)造方法,靜態(tài)代碼塊啥的
二 從程序執(zhí)行的角度
- 首先檢查這個對象有沒有在metaspace(元空間,他是描述對象信息的數(shù)據(jù),在本地內(nèi)存中分配)下,如果沒有,那么就通過雙親委派機(jī)制,去查找這個對象的class,如果有就加載,如果沒有就報錯,ClassNotFoundException,是不是很熟悉
- 分配內(nèi)存空間,找到對象了,就得給對象分配內(nèi)存空間了,計算對象需要空間大小,分配空間
- 設(shè)置默認(rèn)值
- 設(shè)置對象頭
- 執(zhí)行初始化方法,比如構(gòu)造方法。
三 雙親委派
上面提到雙親委派機(jī)制,再簡單說說這個機(jī)制,java的ClassLoader類加載器有三層
- Bootstrap classLoader: 主要負(fù)責(zé)加載核心的類庫(java.lang.*等),構(gòu)造ExtClassLoader和
- ExtensionClassLoader:主要負(fù)責(zé)加載jre/lib/ext目錄下的一些擴(kuò)展的jar。
- ApplicationClassLoader:主要負(fù)責(zé)加載應(yīng)用程序的主函數(shù)類
然后通過一個類分析加載的流程
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{
// 首先,檢查是否已經(jīng)被類加載器加載過
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 存在父加載器,遞歸的交由父加載器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
// 直到最上面的Bootstrap類加載器
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.
c = findClass(name);
}
}
return c;
}
意思就是 加載一個類時,先看看自己能不能加載,如果不能加載就看是否存在父加載器,如果存在就判斷父加載器能不能加載如果能就加載如果不能就繼續(xù)向上,如果到頂了還是不能加載,那就向下,判斷子加載器能不能加載,直到找到能夠加載的子加載器,如果找不到就拋出異常。
為什么要這么干呢,主要是防止危險代碼的植入,比如程序運行時,有人串改了String類的實現(xiàn),那么jvm會判斷這個類有沒有被加載,如果加載了就不回加載這個類,那么就不用擔(dān)心被串改了。
四 類加載
類加載是jvn工作的第一步,他的實現(xiàn)過程有幾步
- 加載 將class文件以流的形式放到內(nèi)存,并轉(zhuǎn)化為Class實例
- 驗證 驗證這些類是否合規(guī),類型是否正確
- 準(zhǔn)備 對靜態(tài)變量分配內(nèi)存,賦值默認(rèn)值
- 解析 解析類,確報對象之間相互引用的正確性
- 初始化 執(zhí)行類構(gòu)造器方法