通常情況下,我們建立一個物件,只需要使用new關鍵字即可。而對於java虛擬機器器來說,需要經歷一系列過程。 首先,需要找到對應的類是哪個,這個類是否已經載入,沒有載入還需要將它先載入進來,然後給將要建立的物件分配記憶體,然後對物件進行初始化設定,我們才能使用一個完整的物件。
經過查詢類的一系列操作之後,java虛擬機器器知道了建立哪個類的物件,這時候需要記憶體來放置將要建立的物件。所以接下來需要分配記憶體,一個類載入進來之後,物件所需要的記憶體已經確定,現在需要找到一塊空閒區域劃分給將要產生的物件。
這種方式需要將使用過的記憶體放在一邊,未使用的放在另一邊,指標指向第一個空閒位置,這時候分配記憶體只需要將指標往空閒位置移動物件大小位置即可。但是我們知道,記憶體有分配和回收,當不斷的進行分配和回收之後,記憶體就不是連續的,如果我們使用這種方式顯然行不通,所以我們需要採用的垃圾處理器帶有空間壓縮整理的功能,例如:Serial,ParNew
還有一種方式,空閒列表方式,根據名稱我們都知道,用一個列表記錄所有的空閒塊,這個需要單獨維護一個列表。CMS這種基於清除演演算法的收集器,就是採用了這個方式。但是有進行進一步的優化,為了能在大多數情況下分配的更快,設計了一個分配緩衝區,從空閒列表拿下一塊較大的記憶體,然後在這塊記憶體內部採用指標碰撞方式來進行分配
物件建立在虛擬機器器中是非常頻繁的行為,即使使用最簡單的指標碰撞方式,僅僅修改指標所指向的位置,在並行情況下也不是執行緒安全的(如果正在給物件A分配記憶體,還沒修改指標位置,物件B也需要分配記憶體,相當於使用錯誤的指標位置)。
一種處理方式是進行同步處理,實際上虛擬機器器採用CAS+失敗重試的方式保證更新操作的原子性。當物件A正在分配記憶體時,標識正在進行分配,物件B來時發現正在分配,則分配失敗,繼續重試,請求分配,直到物件A分配完畢,物件B繼續進行分配。
另一種方式是把記憶體分配的動作按照執行緒劃分在不同的空間中進行,即給每一個執行緒預先分配一小塊記憶體,稱為本地執行緒分配緩衝(Tread Local Allocation Buffer TLAB),哪個執行緒需要分配記憶體時候,現在自己的本地緩衝區中進行分配,不夠了再進行同步鎖定方式。虛擬機器器是否使用TLAB可以通過- XX:+/-useTLAB引數進行設定。
當給物件分配了空間之後,我們需要對物件進行初始化操作,初始化物件的有三個步驟
將除了物件頭以外的所有記憶體空間初始化為零值(如果使用了TLAB,這一步操作可以提前到TLAB分配是進行),這步操作保證了物件的範例欄位在java程式碼中可以不賦值直接使用。
物件頭,既然位於最前面,應該儲存一些描述物件的基本資訊,其中包括該物件是哪個類的範例,如何找到類的元資訊,物件的hash碼(實際不是在這裡進行設定,而是在呼叫Object::hashCode()時計算),物件的GC分代年齡,鎖等資訊。到這一步,在虛擬機器器層面一個新的物件已經產生了。
最後一步,才是按照開發人員真正的意願去構造物件需要的其他資源(父類別等)和狀態資訊,這樣一個真正可用的物件才構建出來。
物件的建立過程很簡單,就是找到建立依據(類),放在哪裡(分配記憶體),初始化(設定我們想要的東西)。