JVM 面試題【初級】

異常和錯誤

有的面試官會挖這個坑:你說說遇到的錯誤java

  • Exception - 是異常,是代碼裏經常寫的
  • Error - 是錯誤,像內粗溢出 OOM 這樣的就是

壞就壞在你可能沒注意說成另外一個了,的確有這樣壞心眼的傢伙(╯‵□′)╯︵┴─┴面試


怎麼判斷2個類是同一個類

  • 類名+包名相同
  • 加載這2個類的類加載器對象必須是同一個

類主動使用方式

類的使用能夠分主動使用、被動使用,區別是被動使用類時,不會致使類的初始化,這會致使不少難以查找的 Bug 的算法

  • 經過new關鍵字、反射、clone、反序列化機制實例化對象
  • 調用類的靜態方法時
  • 使用類的靜態字段或對其賦值時
  • 經過反射調用類的方法時
  • 初始化該類的子類時(初始化子類前其父類必須已經被初始化)
  • JVM啓動時被標記爲啓動類的類(簡單理解爲具備main方法的類)

建立對象的幾種方式

  • new用new語句bai建立對象,這是最常du用的建立對象的方式。
  • Class.newInstance(),JDK9 之後過期了,只能使用public的無參構造方法
  • Construtcor.newInstance(xx) 有參無參均可以,也沒有訪問限制,這就是反射的好處
  • clone()
  • 反序列化手段: 好比IO,ObjectInputStream 的readObject() 方法,從本地文件,或者網絡上獲取
  • 第三方庫Objenesis 利用字節碼技術,動態生成class信息

這個你們瞭解,知道有這麼回事就行,注意反射的2種方式差異,實際場景會用上數據庫


從字節碼的角度看對象的建立

public class Max {
    public void run() {
        String name = new String();
    }
}
複製代碼

字節碼:網絡

public void run();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: invokespecial #3                  // Method java/lang/String."<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 11: 0
        line 12: 8
複製代碼
  • 先走了一個new指令,會判斷這個類加載沒加載,加載完後去堆內存申請一塊內存空間並返回該地址到操做數棧棧頂
  • dup 指令會把剛剛申請到的內存地址複製一份並保存到棧幀中操做數棧棧頂,這樣以後操做數棧實際上就上下有2個相同的內存地址了
  • 執行String對象的初始化方法,這須要地址以便找到對象去執行方法,因此這裏會消耗調一個內存地址的操做數
  • 而後把這個堆內存地址賦值給局部變量表裏的name屬性,因此纔會有dup指令把內存地址複製一邊

一般這個問題是和 volitale 保證有序性、單例模式的雙判斷寫法爲何還要加上 volitale 關鍵字這個問題聯繫在一塊兒問的併發


對象建立的步奏

  1. 判斷對象對應的類是否被加載、連接、初始化了 也就是new指令會去方法區常量池中定位到類的符號引用,檢查這個符號引用是否被加載了
  2. 給對象分配內存空間,計算對象須要佔用多大的內存空間
    • 堆內存規整:指針碰撞
    • 堆內存不規整:JVM要維護一個空閒列表,記錄哪些內存塊可用的,哪塊是用過的,碎片化的問題,對應的是標記清除算法,伊甸園區回收完垃圾以後不作規整整理就這樣,好比早期的CMS垃圾回收器
  3. 併發處理 堆內存是共享區域,就會有併發問題
    • CAS 失敗重試,區域加鎖保證原子性
    • 堆內存給每一個線程預先分配一塊TLAB空間,也就是針對每一個線程預先給一塊專屬的內存空間,以防止併發問題,問題是這塊空間不大,因此還須要上面CAS的配合,能夠經過-XX:UseTLAB來設置
  4. 初始化分配到的內存空間(默認初始化) 也就是給屬性賦一個初始的默認值,即使該屬性在代碼裏設置值了,在這一步也會先給一個默認值,在以後的步奏裏再賦指定值
  5. 設置對象頭 對象頭保存有對象的hashcode,GC信息,鎖信息,所屬類(方法區元數據地址)
  6. 顯示初始化,執行init方法並初始化 具體能夠看下一個問題的解答,init就是類的構造器

實際到代碼spa

Dog dog = new Dog();
複製代碼

new 關鍵字能夠理解爲1-5步,Dog() 至關於第6步線程

指針碰撞:

用過的內存在一邊,沒用過的內存在另外一邊,中間臨界點有一個指針。要是須要新分配內存空間了,只須要把指針移動執定大小便可。不過JVM是否採用這種分配方式,的看採用的垃圾回收器選擇的是否是 Serial、ParNew 這樣的壓縮算法(標記整理)了。核心就是垃圾回收以後新生代伊甸園區的是規整過的才能這麼玩 3d


對象屬性賦值過程

  1. 屬性的默認初始化
    給對象中的屬性賦一個默認值,好比 int age = 100,在這一步 age=0
  2. 顯示初始化/代碼塊初始化
    仍是拿 age 說事,到這裏 age=100 了
  3. 構造器中初始化

上個問題6裏面的init方法對應的就是這裏的二、3部指針


對象訪問的2種方式

有:句柄式、直接指針 2種

1. 句柄式

在JVM堆內存中,維護一個列表,存儲每一個對象對應的內存地址,比如咱們在數據庫搞一個索引表出來,你們要理解句柄的含義,就是索引,句柄是C++的內容,可是在java裏好多地方還有她的身影

句柄的好處在這個索引表的內存地址是不變的,不論是對象的地址怎麼變,對於別人來講其實都是不變的

缺點是真用內存,訪問效率低一些

2. 直接指針

這是目前 htospot 虛擬機訪問對象的方式,不須要索引表,我直接記錄你的內存位置,缺點是地址變了須要通知使用者

這道題仍是知道的好,還真就有人問。具體就是涉及到 GC 的標記清除算法上的優缺點,句柄式的能夠併發的GC垃圾回收,由於句柄是不變的,我恩始終能夠找到對象。直接指針GC咱們就必須中止其餘線程,由於引用地址會變


空對象佔內存多少

這是一道常問的面試題,咱們只考慮通常對象,對象實體是空的,Mark Word 佔64位8個字節

類型指針默認是8個字節的,可是在開啓指針壓縮時會變成4個字節,JDK8 是默認開啓的

參考對齊方式,對象的大小必須能被8整除。因此一個空對象默認佔用內存大小是12個字節,算上對齊方法的化是16個字節

相關文章
相關標籤/搜索