10分鐘快速瞭解 jvm 基礎知識

前言

快速的瞭解Jvm相關知識。

內存模型

開局一張jdk8 JVM圖,然後是JDK低版本的JVM圖。

這裏線程私有的區域有三個:程序計數器、本地方法棧、虛擬機棧。這三個區域隨着線程創建而初始化,線程毀滅而自然的被回收,不需要回收算法來幫助回收。方法區和堆區會被垃圾回收算法回收。但這裏要注意,雖然垃圾回收器不會回收其他區域的對象,但其他區域依舊會發生OOM,仔細想想也可以理解,因爲發生OOM跟垃圾回收根本就是兩回事,OOM是因爲區域沒有內存可用,垃圾回收主要是爲了清空不需要的內存,其他結束的線程產生的垃圾對象可以被回收,但線程還沒執行完而發生的OOM不能回收,不然線程也就執行出問題。

程序計數器

作用:在CPU內部,運行速度最快的區域,因爲多線程環境下,線程可以來回切換,切換後需要找到一開始Java執行的位置,因此程序計數器要線程私有,並且存儲正在上一次執行到的虛擬機字節碼指令的地址,獲得線程後繼續執行下去,執行本地方法該值爲空。

  • 線程:每一個線程都有一個獨立的程序計數器
  • 是否GC:不存在GC情況,唯一不會有OutOfMemoryError 情況的區域

jvm stack 虛擬機棧

作用:方法之間存在調用情況,每個方法在執行的同時都會創建一個棧幀(Stack Frame,是方法運行時的基礎數據結構),用來記錄調用的其它方法,調用方法入棧,方法執行結束出棧。棧幀裏存儲來方法的基本類型的局部變量,例如int、short、boolea等,當然也有一些對象引用存儲在棧裏,一個方法執行結束就會釋放庫存。

  • 線程:每一個線程都有一個獨立的虛擬機棧
  • 是否GC:會
  • StackOverflowError:調用的方法深度過大,拋StackOverflowError異常
  • OutOfMemoryError:擴展棧長度時,內存不夠拋OutOfMemoryError異常,因爲這一塊區域可以是動態的,也可以是固定長度的

問題:

  • 爲什麼Java對象引用在棧裏面
    主要是棧被設計成是一個可以快速訪問的區域,速度很快,通過棧指針比較方便上下移動,所以運行代碼前,Java 虛擬機需要知道棧內所有數據的確切大小及生命週期,這也是爲什麼存儲基本局部變量、對象引用。

  • 上面的Java對象引用具體在那個位置呢?
    答案就是棧幀信息的局部變量表中

  • 棧會不會被垃圾回收?
    我覺得會被垃圾回收,因方法中的基本局部變量是存到棧中的,一個線程執行完來,數據就會被回收,但是一般垃圾回收都發生在Java堆中,所以也很奇怪。

本地方法棧(Native Method Stack)

本地方法棧與虛擬機棧所發揮的作用是非常相似的,本地方法棧則爲虛擬機使用到的 Native 方法服務,JNI 類本地方法最著名的應該是 System.currentTimeMillis() ,本地方法棧區域也會拋出 StackOverflowError 和 OutOfMemoryError 異常。

  • 線程:每一個線程都有一個獨立的本地方法棧
  • 是否GC:會

Java 堆(Java Heap)

此區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存,Java 堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。

  • 線程:線程共有
  • 是否GC:會
  • OutOfMemoryError:會有,GC主要場所

方法區(jdk8叫元數據區)

方法區(Method Area)與 Java 堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然Java 虛擬機規範把方法區描述爲堆的一個邏輯部分,但是它卻有一個別名叫做 Non-Heap(非堆),目的應該是與 Java 堆區分開來。

  • 線程:線程共有
  • 是否GC:會,針對常量池的回收和對類型的卸載。
  • OutOfMemoryError:當方法區無法滿足內存分配需求時,將拋出 OutOfMemoryError 異常。

JDK8 之前,Hotspot 中方法區的實現是永久代(Perm),JDK8 開始使用元空間(Metaspace),以前永久代所有內容的字符串常量移至堆內存,其他內容移至元空間,元空間直接在本地內存分配。

爲什麼要使用元空間取代永久代的實現?

  • 字符串存在永久代中,容易出現性能問題和內存溢出。
  • 類及方法的信息等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出 現永久代溢出,太大則容易導致老年代溢出。
  • 永久代會爲 GC 帶來不必要的複雜度,並且回收效率偏低。
  • 將 HotSpot 與 JRockit 合二爲一。

永久代的垃圾手機主要回收兩部分內容:廢棄常量和無用的類。 回收廢棄常量與回收Java堆中的對象非常相似。以常量池中字面量的回收爲例,若字符串“abc”已經進入常量池中,但當前系統沒有任何String對象引用常量池中的“abc”常量,也沒有其他地方引用該字面量,若發生內存回收,且必要的話,該“abc”就會被系統清理出常量池。常量池中其他的類(接口)、方法、字段的符號引用與此類似。

無用的類需要滿足3個條件:

(1)該類所有的實例都已經被回收,即Java堆中不存在該類的任何實例;
(2)加載該類的ClassLoader已經被回收;
(3)該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

虛擬機可以對滿足上述3個條件的無用類進行回收,此處僅僅是“可以”。

參考博客

Java內存區域(運行時數據區域)和內存模型(JMM)

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