記一次面試題關於對象創建以及內存分配問題

class M{
int n =4;
}
main{
M m = new M();
}

1.對象創建過程
對象初始化過程
1.1申請內存
1.2初始化成員變量 n= 0 //這個時候 m 會指向一個半初始化的對象,因爲n已經初始化
1.3設置初始值 n=4 //m指向一個正確的m對象
2 對象頭具體包括什麼
2.1Mark Word
鎖的信息
Gc標記信息 標記清除算法 GC年齡(4位最大15) 爲什麼標記清除算法標記年齡最大15 對象頭GC標記信息最大4位
hash碼

2.2 klasspointer (指向類的指針) 指向m.class
該指針在32位JVM中的長度是32bit,在64位JVM中長度是64bit。
Java對象的類數據保存在方法區。

2.3 對象內存的存儲佈局

  • 普通對象 new object() 包括
    markword 對象頭 //8 字節
    clas pointer(類型指針) //指向.class
    instance data (實例數據) //m
    padding(數據對齊)
  • 數組對象
    對象頭
    類型指針
    數組長度(4字節)
    實例數據
    對齊

2.4 DCL單例(double check lock) 是否需要volatile 關鍵字
懶漢式單例:

public class Singleton {
 
    private static Singleton singleton;
 
    private Singleton () {}
    
    public static  Singleton getInstance () {
        if (singleton == null) {
            singleton = new Singleton ();
        }
        return singleton;
    }
}

這個在單線程情況下可以保證內存中只存在一個實例,多線程下就不能保證;
所以需要加鎖來解決

public class Singleton {
 
    private static Singleton singleton;
 
    private Singleton () {}
    
    public static synchonized Singleton getInstance () {
    	//業務代碼
        if (singleton == null) {
            singleton = new Singleton ();
        }
        return singleton;
    }

這樣加鎖比較重 ,業務代碼也不能執行
優化;

public class Singleton {
 
    private static Singleton singleton;
    
    private Singleton () {}
    
    public static Singleton getInstance () {
        if (singleton == null) {
            synchonized (Singleton.class) {
              if (singleton == null) {
                singleton = new Singleton ();
                }
            }
        }
        return singleton;
    }
}

但是這個有能可會發生指令重排序問題,就是實例在創建過程中,cup會把對象創建幾個步驟重排序執行,;比如兩個線程來實例化 Singleton 對象, a線程只實例化一半時候,只是初始化了 單例中的某個成員變量 n=0 時, 這個時候停止了, b線程進來發現 Singleton 是一個半初始化狀態,是有指向的引用, 就給其返回; 而a繼續執行會實例化一個與b線程不一樣的實例;
在這裏插入圖片描述
這個時候就需要給
private static Singleton singleton; 添加關鍵詞 volatile
.volatile 的特性有

  1. 線程可見性
    所謂“可見性”,是指當一個線程修改了這個變量的值,新值對於其他線程來說是可以立即得知的。(通過主內存,一個線程修改了共享變量,新值立即同步到主內存,其他線程讀取該值時從主內存中拉取)
    由於volatile變量只能保證可見性,在不符合以下兩條規則的運算場景中,我們仍然要通過加鎖來保證原子性。

(1)運算結果並不依賴變量的當前值,或者能夠確保只有單一的線程修改變量的值。
(2)變量不需要與其他的狀態變量共同參與不變約束(if(a>b)這種形式)
2. 保證long和double類型變量的原子性。
3. 禁止指令重排序
進而保證有序性。Java中天然的有序性可以總結爲一句話:如果在本線程內觀察,所有的操作都是有序的;如果在一個線程中觀察另一個線程,所有的操作都是無序的。

故最終改進

public class Singleton {
 
    private static volatile  Singleton singleton;
    
    private Singleton () {}
    
    public static Singleton getInstance () {
        if (singleton == null) {
            synchonized (Singleton.class) {
              if (singleton == null) {
                singleton = new Singleton ();
                }
            }
        }
        return singleton;
    }
}

2.5 class對象是在堆還是在方法區
創建的Class實例在java heap中
請具體參考 https://www.jianshu.com/p/6e039f83a24b
2.6對象怎麼定位
直接 直接指針
間接 句柄方式
在這裏插入圖片描述
2.7對象內存怎麼分配
當對象大小比較小的時候,直接在棧空間中,對象比較大後,直接進入old區,如果不夠大,進入Eden區 ,GC清理,如果活着,進入survive1, GC再清理,還活着進入survive2,GC繼續清理如果年齡到了進入old區;
2.8 object o = new object();佔多大內存
16字節
最後附一個學習的連接講的很詳細 https://www.jianshu.com/p/76959115d486

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章