類的載入過程,分為載入,連線和初始化,而連線又分為三個過程,驗證準備和解析
其中載入部分,簡而言之,通過類載入器,將.j**a檔案載入為.class檔案存入jvm記憶體
連線部分主要是對.class檔案在執行器前做一些修飾性的工作
驗證部分:保證.class檔案的內容符合虛擬機器要求的規範,不會存在安全性問題,例如是否修改了final方法,是否引用了看不見的函式(private),是否存在不合理的型別轉換等等
準備部分:這個部分主要是對.class檔案的類變數(注意不是例項變數,即static變數)做乙個初始化歸零操作,並且分配記憶體,注意這裡的初始化和後面的初始化不同,這裡是將所有變數根據不同的變數屬性而賦予不同的值,例如int為0,string為null,不會賦予類宣告時的初始值,但是例如final static int a=456,這個時候a的值還是為456,總而言之:常量型變數的初始值為宣告時的值(例如a)
引用型則初始為null
8種基本型別則預設為0
解析部分:這個部分是對class檔案中的符號引用替換為直接引用。符號引用則為乙個符號,可以唯一標識乙個函式。
直接引用則為該函式的位址
例如,乙個函式a(),其位址為1234(函式在方法區中的偏移量),此時的符號引用為a,而直接引用為1234,當我們在j**a**中呼叫函式a,經過這一步之後就會直接替換為1234
最後乙個初始化部分:初始化部分即將類中的類變數賦值(非final的static變數),如果父類沒有初始化過,那麼就先初始化父類。
以上即為簡單的類載入過程的介紹。
eg:類載入的時機:
new,或者呼叫某個class的靜態變數,函式
反射時如果載入乙個類時,其父類還沒有載入,那麼會去先載入他的父類
jvm啟動的時候會去先載入乙個子類
動態**(其實就是反射)
注意:以下並不會載入類:
通過子類來引用父類的靜態變數,不會載入子類
引用乙個類的靜態常量
建立乙個該類的陣列,因為其實現在底層還是object
第一步就是先去常量池是否能夠定位到這個類的引用,如果找到再看看這個類是否已經載入過了,如果此時對應的類沒有載入,會去先載入這個類,載入的過程參考上面的過程。
如果此時已經載入過,那麼這個類對應的物件大小其實已經知道,再去堆區中劃分記憶體即可。這裡劃分記憶體其實就有幾種方法,主要是根據垃圾收集器的不同而不同當垃圾收集器使用「標記-清除」時,那麼就應該使用空閒列表法,儲存哪些塊是可用的(因為這個時候記憶體會分為很多快,零碎)
而當垃圾收集器使用「標記整理」或者「標記複製時」,那麼就會使用指標碰撞,這個主要就是因為記憶體是連續的,不會產生內碎片,可以直接根據已經用的記憶體尾部直接開始劃分一塊記憶體區域。
注意:這裡記憶體分配其實還要考慮多執行緒的問題,有兩種解決方法,第一種是加cas鎖,分配失敗就再次分配,第二種是每個執行緒先去拿一些可以用的記憶體空間放在自己的緩衝池中(tlab),等到實際用的時候就直接分配,用完了再去申請(類似於我們之前生成分布式id的時候使用的方法),這樣就解決了多執行緒的問題。
物件記憶體分配空間的清零操作(j**a物件主要由物件頭,示例資料和對其填充組成,其中物件頭不會被清零)
虛擬機器對該物件做一些資料的複製(主要是物件頭,例如gc年齡,分代資訊,鎖標誌位,hashcode之類的)
執行物件的init()初始化方法,給物件的屬性賦值
將該物件的位址返回給棧中對應呼叫的引用。
new乙個物件發生什麼?
doctype html en utf 8 viewport content width device width,initial scale 1.0 x ua compatible content ie edge document title head 物件直接量 自變數 var zs new o...
new 乙個物件時發生了什麼
var person function name var p new person boring 以上 在呼叫時,會變成如下 var p person name newobj.constructor.call newobj,name 3 return newobj 4 0 建立乙個新的物件,newo...
物件 new乙個
1格式格式 class 類名 類名 要求符合大駝峰命名法,見名知意 類名這裡可以認為是乙個資料型別名,自定義資料型別 屬性描述 描述當前類有哪些屬性 例如 人類的身高體重,年齡姓名 行為描述 描述當前類有哪些行為 例如 人類的吃飯行為,睡覺行為,打遊戲行為依葫蘆畫瓢格式 scanner掃瞄器 sca...