天天看點

Java記憶體模型(闆塊劃分)

為了春招,系統地複習一下Java相關的知識,應該不會寫源碼解析,完全是給自己看的,如果足夠幸運,有幸進入大廠,重新寫下給别人看的

注意點

  • JVM的版本很多,細節多有不同,這裡說的是Hotspot VM.
  • 最主要的就是Heap,也就是堆.堆是線程共享的
    • 堆主要分為年輕代(Young)和老年代(Old),正常的預設值是,年輕代:老年代 = 1/4~1/3 ;寫下主要是為了防止被問到,其實沒什麼意義,因為這個比例不同版本都會不同,而且我相信大廠一定會根據自己的業務修改這些參數或者指定自己的虛拟機.
    • 年輕代分為兩個部分,三個闆塊,即"1和Eden區 + 2 個 Survivor區",比例預設值是8:1:1
      • Eden , 也就是伊甸園,有種萬物起源的感覺,在JVM中,也就是 剛被建立的對象存放的地方.不過當對象的大小大于一個閥值,就會被放在Old區.這個閥值可以通過參數設定,參數部落格再說.
      • 2個Survivor區通常被稱為s0,s1,理論上二者是一樣大的.翻譯過來就是幸存者,其實在JVM中也有這樣的意思,當Eden區滿的時候,JVM會做Young GC(Minor GC),也就是從Eden中找出還存活的對象,如何判斷對象是否還存活,需要另開一個部落格說.活下來的對象,就要進入s0或者s1(二者都可以),如果選擇了s0,所有活着的對象就會放在s0中,如果s0滿了,剩下的對象會被放在old區,然後會将Eden區清空,當Eden區再滿的時候,會将s0和Eden區中活着的對象放在s1中,(同樣的,放不下就放在old區),然後清空Eden和s0,然後反複進行.如果經過多次Minor GC還活着的對象,就會被放在old區,這個反複也是有預設值的,是15次,但還是上面說的關于預設值的問題.
    • 進入old區有多種方式(比如s0或s1滿了,又或者反複多次minor gc還存活),但無論以何種方式進入,都是同樣對待的.old區域的對象會被認為是長壽的.而old區隻有進行FULL GC才會被清理,但FULL GC其實是很不好的,進行Full GC時,所有正在進行的工作都要停止,厲害的虛拟機也展現在對于高并發情況也可以很久做一次FULL GC.
  • 虛拟機棧,線程私有
    • 虛拟機棧主要描述了Java方法執行時的記憶體模型,每個方法在執行的同時都會建立一個棧幀,用于存放局部變量表,操作數棧,動态連結,方法出口等資訊,每一個方法從調用到執行完成的過程,就是對應棧幀入棧出棧的過程
    • 虛拟機棧有一個常見的異常,即使

      StackOverFlowError

      ,這個異常大家可能在使用遞歸時遇見的,因為遞歸是方法内調用方法,如果遞歸層次太深,所有的方法棧幀都無法出棧,自然就會棧溢出,這也是我不喜歡使用遞歸的原因.
  • 方法區 , 方法區是線程共享的,用于存放已被虛拟機加載的類資訊(Class對象),常量(常量池内),靜态變量,即時編譯器編譯後的代碼等資料.
  • 永久代–元空間,方法區的實作.
    • 永久代在1.8就被移除了,代替的是元空間,其實從1.7就開始了,很多永久代的東西在1.7就開始移到堆中,但是還儲存這這塊區域,到了1.8徹底移除了.但是還是要知道的.
    • 永久代和元空間是方法區的實作,這個怎麼說,就是方法區是那麼一塊區域,但是也是需要回收記憶體,是以就使用堆空間的分代收集擴充到了方法區,然後就将實作回收後的方法區稱為永久代,但這僅限于HotSpot,因為其他虛拟機都不存在永久代這個概念.
    • 雖然垃圾回收在方法區中很少見,但是還是有的,主要是随常量池和類型的解除安裝.
    • 永久代和元空間在很多方面還是相似的,主要差別就是元空間不在虛拟機内,而是在本地記憶體中
  • 運作時常量池是方法區的一部分,用于存放編譯期生成的各種字面量和符号引用,我們可以接觸到的就是String類的intern方法,可以将一個String字面量作為常量放入常量池中,常量池作為方法區的一部分,也受到方法區大小的限制,當滿了以後會抛出OOM.
  • 剩下還有程式計數器和本地方法棧,都是很難接觸到的部分,提一句就行了.