JVM面試知識點梳理

本文這個只是我本身整理的面試中多面到的一些JVM方面的知識點,就這樣看它們都是孤立的,我是看《深刻Java虛擬機》以及極客時間一個JVM系列的文章,才把這些概念給貫通的。java

好比一個知識點:垃圾回收分Minor GC、Full GC。Minor發生在新生代,新生代分Eden、Survivor,而Survivor又分 From Survivor、To Survivor。新生代採用的是Copy機制的垃圾回收算法,經過空間換時間,它是怎麼換的呢?To Survivor是一塊空的內存,垃圾回收時,會把Eden、From Survivor中的可達的(區別堆外不可達)對象搬到 To Survivor中去,而後清除Eden、Form Survivor,整個過程接受後,交換From跟To,這期間有可能把對象從Eden、From搬去To的時候,To內存不足則須要借用老生代的內存,這就是所謂的內存擔保。面試

Java內存結構

程序計數器:每一個線程執行程序指令的行號算法

虛擬機棧:存放每一個方法的棧幀,幀的入棧跟出棧就是方法執行的過程緩存

本地方法棧:Native方法的棧安全

Java堆:保存Java對象的地方,細分爲 Eden區, From Survivor空間, To Survivor空間(線程共享)多線程

方法區:線程共享,存放已經被虛擬機加載進來的類信息,常量、靜態變量,JIT編譯後的數據代碼。java的class文件首先進入的到方法區裏面去。app

運行時常量池

方法區的一部分。優化

四大引用

強引用:只要強引用還在,引用的對象永遠不會被回收線程

軟引用: 發生內存溢出以前會清除orm

弱引用:垃圾回收的時候會收走

虛引用:對象生命週期不會受到它影響,只是在被回收掉的時候,收到通知。

對象的存活
  • 引用計數器
  • 可達性算法(堆外的引用, GC Roots)
垃圾回收算法
  • 標記清除法
  • 複製算法
  • 標記整理法
  • 分代收集
安全點、Minor GC、Full GC、 STW、TLAB

safePoint、安全區域 。(不操做內存的寫,不跟主存打交道) STW(stop the world)

eden, survior中調用 Minor GC,之因此沒有用STW,添加了統計老生代到 新生代數據引用的卡表(髒位)

新生代(eden + from + to)TLAB(Thread Local Allocation Buffers)因爲to Survior的空間不夠,找老年代作內存擔保

當經歷過幾回 從from 到 to過程後依舊活着的對象就能夠進入到 老生代中去了。

垃圾收集器
  • Serial (單線程,新生代,copy算法)

  • Parallel New(多線程,新生代,copy算法,時間優先)

  • Parallel Scavenge(多線程,新生代,copy算法,吞吐量優先)

  • Serial Old(單線程,老生代, 標記清除)

  • Parallel Old(多線程,老生代, 標記清除)

  • CMS:垃圾收集器跟應用並行(多線程,老生代, 標記清除)分兩次清理,第一次清理的時候,工做線程跟垃圾線程並行,第二次STW。(Java 9 被廢除)

  • G1 (新生代 + 老生代)

類加載過程
  • 加載:驗證Class文件是否按Class的結構走的,而後加載完成以後變成內存的結構
  • 驗證:不會等加載結束,其實交替進行的(文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證)
  • 準備:分配內存,虛方法動態分配的方便發表。建立符號引用(因此跟驗證是交叉的)
  • 解析:(符號引用到直接引用:字段解析、類方法解析、接口方法解析)
  • 初始化 (cinit<>)
即時編譯(JIT優化)

熱點方法,循環熱點

C、C++屬於靜態編譯,運行時候不會。java屬於動態編譯,虛方法動態分配,激進優化等使之更完全,可是佔用CPU。

優化方式:

  • 內聯優化(省去了方法的加載,壓棧等。詭異的TR1圖)

  • 逃逸分析(對象沒有被方法外引用的時候,直接到接近CPU的棧上分配存儲空間。方法僅被一個線程調用到的時候,去掉同步鎖。)

  • 指令集(Compare And Sweep, Compare and Set),???

  • 循環優化、向量優化

方法調用指令
  • invokespecial(實例構造方法、私有方法、父類方法)
  • invokestatic(靜態方法,靜態分配)
  • invokevirtual(調用全部虛方法)
  • invokeinterface(調用接口方法,運行時才)
  • invokedynamic (方法句柄 MethodHandler, Lookup 不一樣於 反射,修改方法棧中的指令集)
反射的消耗
  • Method.invoke(Class, Object[])方法中只可以是 Object類型,沒有基本數據類型。自動裝箱、拆箱損耗。
  • Method.invoke沒有辦法內聯了。
  • 沒有辦法逃逸分析。
  • 要進行方法找尋,還要往上查找父類中的全部方法。存儲記錄
  • 本地實現、動態實現(超過必定的次數,動態實現會快一些)
Volatile
  • 修改內存可見性,沒有用線程中的寄存器做爲緩存
  • 禁止重排序,設置內存屏障(讀讀,讀寫,寫讀, 寫寫),添加指令 lock … 空指令,讓lock指令後的指令無法重排到前面去(單線程裏面無論 數據相關性、happens—before)
線程、鎖
  • 偏向鎖 (當第一次進入到同步代碼塊的時候,不上鎖,修改對象頭中的Mark word,線程的ID等信息,經過CAS指令,全程只有一個線程,因此記錄它偏向它,偏向鎖)

  • 輕量級鎖 (只有一個線程,把線程上鎖記錄加入到 對象頭中去,採用CAS,再也不是第一個線程,而是另外的線程不一樣時間段執行同步代碼塊,有偏向鎖到 輕量級鎖)

  • 重量級鎖(一個線程在執行,另外一個線程來競爭。輕量級鎖 鎖膨脹爲重量級鎖)等待的線程沒有直接去wait,而是運行空指令處於就緒狀態,等待運行中的線程釋放鎖,而後本身拿取,這樣的叫自旋鎖。爲何要添加這個自選狀態呢?

    當線程一旦進入到wait後,就統一交由CPU進行管理 + 調度,轉爲內核態,到時候喚醒運行變爲用戶態,這個過程的切換有不少資源的 消耗(內存的拷貝複製等),在這裏運行空指令消耗可能比這個切換更加省CPU。可是對於其餘鎖而言就失去公平性了。

    這樣相比而言,協程有本身管理、調度部區份內核態、用戶態,資源開支比較小,是現代語言(Kotlin等)中經常使用的技術。(搶佔式調度、協同式)

synchronized(monitor, monitorenter, monitorexit)

ReentrantLock(重入鎖), NewCondtion

相關文章
相關標籤/搜索