一.對象的創(chuàng)建
虛擬機遇到NEW指令時,先檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載,解析和初始化過。如果沒有,則必須先執(zhí)行類加載過程。
對象在堆中分配內存的方法
1.指針碰撞法
如果java堆中的內存是規(guī)整的,所有用過的內存放在一邊,空閑的內存放在一邊,中間一個指針作為指示器,那分配內存就是把指針往空閑的內存空間挪動與對象的大小相等的距離。
2.空閑列表
如果java堆并不規(guī)整,虛擬機會維護一個列表來記錄哪些內存是可用的,在分配的時候從列表中找到一個足夠大的空間劃分給對象實例,并更新列表上的記錄。
選擇哪種分配方式是有java堆是否規(guī)整決定,java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。
但是需要提醒的是比如指針碰撞法需要考慮在并發(fā)的情況下創(chuàng)建對象,可能正在給對象A分配內存,對象B又使用了原來的指針分配內存的情況,解決方案:一種是對分配內存空間的動作進行同步處理--實際上虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性(CAS的基本思想是認為當前環(huán)境中的并發(fā)并沒有那么高,比較樂觀的看待整個并發(fā),只需要在更新某個值時先檢查下該值有沒有發(fā)生變化,如果沒有發(fā)生變化則更新,否則放棄更新,即如果當前的內存值等于原始值,則更改為修改值)另一種是把內存分配的動作按照線程劃分在不同的空間之中,即每個線程都要在java堆中預先分配一小塊內存,稱為本地線程分配緩沖。
內存分配完后虛擬機需要把分配到的內存空間都初始化為零值(不包括對象頭),接下來要對對象進行設置,例如這個對象是哪個類的實例,如何才能找到類的元數(shù)據(jù)信息,對象的哈希碼,對象的GC分代年齡等信息。這些信息存在對象頭中。
二.對象的內存布局
對象在內存中的存儲可以分為對象頭,實例數(shù)據(jù),對齊填充
其中對象頭包括兩部分數(shù)據(jù),一是用于存儲對象自身的運行時數(shù)據(jù),二是類型指針,虛擬機通過這個確定對象是哪個類的實例。
實例數(shù)據(jù)是對象真正存儲的有效信息,也是在程序代碼中定義的各種類型的字段內容。
對齊填充并不一定存在,起定位符的作用
三.對象的訪問定位
對對象的訪問分為使用句柄和直接指針訪問兩種方式
使用句訪問的話,JAVA堆中會劃分出一塊內存作為句柄池,reference(棧中)存儲的的對象就是對象的句柄地址,而句柄中包含了實例數(shù)據(jù)和類型數(shù)據(jù)各自的地址。
優(yōu)勢是對象移動時不需改變reference,只需改變句柄中實例數(shù)據(jù)的指針。

如果使用直接指針訪問,java堆對象中就必須放置訪問類型數(shù)據(jù)的相關消息,reference中存儲的就是對象地址。
好處是速度更快。
