java.jvm.自動內(nèi)存管理機(jī)制.hotspot虛擬機(jī)對象.對象創(chuàng)建

  • 對象創(chuàng)建

這里的對象僅僅是普通java對象,不包括數(shù)組摇天、Class對象

package com.wkh;

public class Test {
   public Test(int i, String bb) {
       
   }
}
package com.wkh;

public class Main {

   public static void main(String[] args) {
       new Test(23,"bb");
   }
}
  • public static void main(String[] args)的class字節(jié)碼
        0: new           #2                  // class com/wkh/Test
        3: dup
        4: bipush        23
        6: ldc           #3                  // String bb
        8: invokespecial #4                  // Method com/wkh/Test."<init>":(ILjava/lang/String;)V
        11: pop
        12: return


  • 對象創(chuàng)建過程

當(dāng)虛擬機(jī)遇到new指令的時(shí)候

  1. 首先去檢查這個(gè)指令的參數(shù)(即#2)是否能在常量池中定義到一個(gè)類的符號引用
  2. 檢查這個(gè)符號引用代表的類是否已經(jīng)被加載、解析和初始化過并炮,如果沒有坤溃,那必須先執(zhí)行相應(yīng)的類加載過程
  3. 類加載檢查通過后,接下來虛擬機(jī)將在為新生對象分配內(nèi)存昧互。對象所需內(nèi)存的大小在類加載完成后便可以確定挽铁,為對象分配空間的任務(wù)等同于把一塊確定大小的內(nèi)存從java堆中劃分出來。
  4. 內(nèi)存分配完成后敞掘,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零(不包括對象頭)叽掘,如果使用TLAB,這一操作也可以提前至TLAB分配時(shí)進(jìn)行玖雁。這一步操作保證了對象的實(shí)例字段在java代碼中不賦初值就可以直接使用更扁,程序能訪問到這些字段的數(shù)據(jù)類型所對應(yīng)的零值。
  5. 接下來,虛擬機(jī)對對象進(jìn)行必要的設(shè)置疯潭,例如這個(gè)對象是哪個(gè)類的實(shí)例赊堪、如果才能找到類的元數(shù)據(jù)信息面殖、對象的哈希碼竖哩、對象的GC分代年齡等信息。這些信息存放在對象的對象頭(Object Header)之中脊僚。根據(jù)虛擬機(jī)當(dāng)前運(yùn)行狀態(tài)的不同相叁,如是否啟動偏向鎖等,對象頭會有不同的設(shè)置方式辽幌。

說明:1-5new指令的過程

  1. 上面的工作完成后增淹,從虛擬機(jī)的視角來看,一個(gè)新的對象已經(jīng)產(chǎn)生了乌企,但從java程序的視角來看虑润,對象創(chuàng)建才剛剛開始,因?yàn)?code><init>方法還沒有執(zhí)行加酵,所有的字段都是零值拳喻。所以,一般來說猪腕,執(zhí)行new指令之后冗澈,還會接著執(zhí)行<init>方法,如上面的8: invokespecial #4 // Method com/wkh/Test."<init>":(ILjava/lang/String;)V陋葡。
  • 下面是對象創(chuàng)建過程new指令的源碼

等同于對象創(chuàng)建過程1-5

//jdk7u-dev/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp
//確保常量池中存放的是已解釋的類亚亲。
if (!constants->tag_at(index).is_unresolved_klass()) {
 // 斷言確保是 klassOop 和 instanceKlassOop 
 oop entry = constants->slot_at(index).get_oop();
 assert(entry->is_klass(), "Should be resolved klass");
 klassOop k_entry = (klassOop) entry;
 assert(k_entry->klass_part()->oop_is_instance(), "Should be instanceKlass");
 instanceKlass* ik = (instanceKlass*) k_entry->klass_part();
 // 確保對象所屬類已經(jīng)經(jīng)過初始化階段
 if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
  // 取對象長度
   size_t obj_size = ik->size_helper();
   oop result = NULL;
   // 如果TLAB沒有把內(nèi)存中的值置零,那么這里就需要置零
   bool need_zero = !ZeroTLAB;
  // 是否使用TLAB腐缤,是否在TLAB中分配對象
   if (UseTLAB) {
     result = (oop) THREAD->tlab().allocate(obj_size);
   }
   if (result == NULL) {
     need_zero = true;
     // 直接在垃圾收集器中的eden空間分配對象
retry:
     HeapWord* compare_to = *Universe::heap()->top_addr();
     HeapWord* new_top = compare_to + obj_size;
    // cmpxchg是x86中的CAS指令捌归,這里是一個(gè)C++方法,通過CAS方式(同步)分配空間岭粤,如果并發(fā)失敗惜索,轉(zhuǎn)到retry中重新嘗試,直至成功分配為止绍在。
     if (new_top <= *Universe::heap()->end_addr()) {
       if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
         goto retry;
       }
       result = (oop) compare_to;
     }
   }
   if (result != NULL) {
     // 如果需要门扇,則為對象初始化零值
     if (need_zero ) {
       HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
       obj_size -= sizeof(oopDesc) / oopSize;
       if (obj_size > 0 ) {
         memset(to_zero, 0, obj_size * HeapWordSize);
       }
     }
    // 根據(jù)是否啟用偏向鎖來設(shè)置對象頭信息
     if (UseBiasedLocking) {
       result->set_mark(ik->prototype_header());
     } else {
       result->set_mark(markOopDesc::prototype());
     }
     result->set_klass_gap(0);
     result->set_klass(k_entry);
    // 將對象引入棧,繼續(xù)執(zhí)行下一條指令
     SET_STACK_OBJECT(result, 0);
     UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
   }
 }
}
  • java堆分配內(nèi)存的2種方式
  1. 指針碰撞(Bump the Point)
    如果java堆中內(nèi)存是絕對規(guī)整的偿渡,用過的內(nèi)存放一邊臼寄,空閑的內(nèi)存放另一邊,中間放著指針作為邊界溜宽,那么分配內(nèi)存僅僅是把指針向著空閑那邊挪動一段與對象大小相等距離吉拳,這種分配方式叫做指針碰撞
  2. 空閑列表(Free List)
    如果java堆不是絕對規(guī)整的适揉,已使用和空閑的內(nèi)存交錯(cuò)留攒,那么虛擬機(jī)就必須維護(hù)列表煤惩,上面記錄哪些內(nèi)存塊是可用的,在分配的時(shí)候找一塊足夠大空間劃分給對象實(shí)例炼邀,并且更新表上記錄魄揉,這種分配方式叫做空閑列表
  3. 采用哪種內(nèi)存分配方式
    選擇哪種分配方式是由java堆是否規(guī)整決定的拭宁,而java堆是否規(guī)整由垃圾收集器是否帶有壓縮整理功能決定洛退。因此,在使用Serial杰标、ParNew等帶Compact過程的收集器時(shí)兵怯,系統(tǒng)采用指針碰撞;而使用CMS這種基于Mark-Sweep算法的收集器時(shí)腔剂,通常采用空閑列表媒区。
  • 關(guān)于java堆分配內(nèi)存時(shí)線程不安全的的問題
  1. 例子
    正在給對象A分配內(nèi)存,指針還未進(jìn)行修改掸犬,B同時(shí)又使用了原來的指針分配內(nèi)存袜漩。
  2. 解決辦法1-同步
    對分配內(nèi)存空間的動作進(jìn)行同步處理,實(shí)際上虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性
  3. 解決辦法2-TLAB
    把內(nèi)存分配的動作按照線程劃分在不同的空間中進(jìn)行登渣,即每個(gè)線程在java堆中預(yù)先分配一小塊內(nèi)存噪服,稱為線程私有分配緩沖(Thread Local Allocation Buffer,TLAB)胜茧。
    哪個(gè)線程需要分配內(nèi)存粘优,就在哪個(gè)線程的TLAB上分配,只有TLAB用完并分配新的TLAB時(shí)呻顽,才需要同步鎖定雹顺。虛擬機(jī)是否使用TLAB,可以通過-XX:+/-UseTLAB參數(shù)設(shè)定廊遍。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嬉愧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喉前,更是在濱河造成了極大的恐慌没酣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卵迂,死亡現(xiàn)場離奇詭異裕便,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)见咒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門偿衰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事下翎$脱裕” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵视事,是天一觀的道長胆萧。 經(jīng)常有香客問我,道長郑口,這世上最難降的妖魔是什么鸳碧? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮犬性,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腾仅。我一直安慰自己乒裆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布推励。 她就那樣靜靜地躺著鹤耍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪验辞。 梳的紋絲不亂的頭發(fā)上稿黄,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死办铡,一個(gè)胖子當(dāng)著我的面吹牛槽惫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粹污,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了互纯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤磕蒲,失蹤者是張志新(化名)和其女友劉穎留潦,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辣往,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兔院,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了排吴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秆乳。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屹堰,到底是詐尸還是另有隱情肛冶,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布扯键,位于F島的核電站睦袖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荣刑。R本人自食惡果不足惜馅笙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厉亏。 院中可真熱鬧董习,春花似錦、人聲如沸爱只。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恬试。三九已至窝趣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間训柴,已是汗流浹背哑舒。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幻馁,地道東北人洗鸵。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像宣赔,于是被迫代替她去往敵國和親预麸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容