1. 程式人生 > >JAVA虛擬機器結構之執行時資料區

JAVA虛擬機器結構之執行時資料區

  • jvm的執行時資料區根據用途一共可以分為這幾類:pc寄存機,java虛擬機器棧,java堆,方法區,執行時常量池,本地方法棧。其中java堆,方法區,執行時常量是公有的資料區,隨著虛擬機器的啟動而建立,隨著虛擬的退出而銷燬。而pc暫存器,java虛擬機器棧,本地方法棧則是執行緒私有的,隨著執行緒的開始和結束而建立和銷燬,執行時資料區如圖下。


    這裡寫圖片描述

PC(Program Counter)暫存器

  • 每個執行緒啟動的時候,都會建立一個PC(Program Counter,程式計數器)暫存器。PC暫存器裡儲存有當前正在執行的JVM指令的地址。 每一個執行緒都有它自己的PC暫存器,也是該執行緒啟動時建立的。儲存下一條將要執行的指令地址的暫存器是 :PC暫存器。PC暫存器的內容總是指向下一條將被執行指令的地址,這裡的地址可以是一個本地指標,也可以是在方法區中相對應於該方法起始指令的偏移量。
  • JVM的多執行緒是通過執行緒輪流切換分配執行時間來實現的,在任何時刻,每個處理器都只會執行一個執行緒中的指令,當執行緒進行切換的時,為了執行緒能恢復當正確的位置,所以每個執行緒必須有個獨立的執行緒計數器,這樣才能保證執行緒之間不互相影響;
  • 如果該執行緒當前執行的方法是native的,對應的暫存器的值是undefined的,否則就是將要執行的指令地址;
  • pc暫存器也是為一個不會出現記憶體溢位問題的資料區;
java虛擬機器棧

  • 這個也是一個執行緒私有的,生命週期與執行緒是同步的,每個方法在執行的同時,都會建立一個棧幀(棧幀詳解),用於儲存區域性變量表,運算元棧,動態連結,方法出入口等資訊。每個方法的呼叫到執行完成的過程就是一個棧幀入棧到出棧的過程;
  • java虛擬機器棧的除了棧幀的出棧入棧之外,不會再受到其他引數的影響,所以java虛擬機器棧所使用的記憶體不需要保證是連續的;
  • 我個人的理解,在某種程度上,jvm虛擬機器是以棧幀(方法)為基本元素,java虛擬機器棧為載體,來解釋並執行我們的程式的;所以雖然我們是面向物件式的程式設計,但實際解釋執行時仍就只是面向過程式的執行?
  • java虛擬機器棧可能會出現的異常:
    • 執行緒請求棧的深度大於虛擬機器棧所允許的深度,這時候將會丟擲StackOverflowError異常
    • 當Java虛擬機器允許動態擴充套件虛擬機器棧的時候,當擴充套件的時候沒辦法分配到記憶體的時候就會報OutOfMemoryError異常
本地方法(Native)棧

  • 本地(native)方法:可以理解為不是java語言寫的方法程式碼,而是C,C++等其他語言寫的程式碼。java語言提供了機制去呼叫其他語言實現的方法的介面,這需求也是我們在實際業務中會碰到的,所需要的。而非java語言寫的方法對應與java統稱為本地(native)方法。
  • java虛擬機器需要單獨用到本地方法棧來執行native方法,一般就是傳統的棧(通常稱為C stack),有的虛擬機器(Sun HotSpot)也會將本地方法棧與java虛擬機器棧合併實現,而不需要單獨實現本地方法棧。另java虛擬機器規範中好像也沒有規定一定要支援本地方法棧,支援native方法;
  • 可以看出本地方法棧和java虛擬機器棧的功能幾乎是一樣的,只是物件不同;
方法區(method area)

  • 方法區是可供各個執行緒共享的執行時記憶體區域,方法區域傳統語言的編譯程式碼儲存區的作用非常類似。它儲存每一個類的結構資訊,例如執行時常量池,欄位,方法資料,建構函式等等;
  • 方法區我是這樣理解,方法區裡面存的是“類”的結構資訊和一些靜態的資料,在java堆中儲存的是物件例項。而我們實際要用建立物件例項時,就依賴與方法區裡面的資料;
  • 方法區的大部分資料(類資訊)其實在編譯階段就已經確定下來了,所以我們可以選擇固定大小或者擴充套件的大小,也可以選擇不在這個記憶體區域內實現垃圾收集和壓縮,所以一般我們也稱方法區為永久代;
  • 方法區的大小由-XX:PermSize和-XX:MaxPermSize來調節;
  • JDK8之後,取消了永久代,提出了元空間,並且常量池、靜態成員變數等遷移到了堆中;元空間不在虛擬機器記憶體中,而是放在本地記憶體中;
執行時常量池

  • 這個區域屬於方法區。但由於功能特殊,也單獨介紹;
  • 該區域存放類和介面的常量,除此之外,它還存放成員變數和成員方法的所有引用。當一個成員變數或者成員方法被引用的時候,JVM就通過執行常量池中的這些引用來查詢成員變數和成員方法在記憶體中的的實際地址;
  • 常量池主要用於存放編譯生成的各種字面量和符合引用,由於常量池屬於方法區的一部分,所以當常量池沒有記憶體空間的時候就丟擲OutOfMemoryError異常;
JAVA 堆

  • 堆區是Java虛擬機器所管理的記憶體中最大的一塊,Java堆是被所有執行緒共享的記憶體區域,主要儲存物件的例項、陣列等;
  • java 堆也是垃圾回收的主要區域,為了方便垃圾回收再java堆的設計上還會根據垃圾回收的演算法進行相應的劃分,如新生代,老年代等;
  • 當堆中沒有記憶體完成例項分配,並且堆無法擴充套件的時候,將會丟擲OutOfMemoryError異常;
  • java 堆所使用的記憶體不需要保證是連續;
直接記憶體(Direct Memory)

  • 直接記憶體並不是虛擬機器執行時資料區的一部分,也不是虛擬機器規範中定義的記憶體區域。在JSK1.4中新加入NIO類,引入了一種基於通道與快取區的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然後通過DirectByteBuffer物件作為這塊記憶體的引用進行操作。這樣在一些場景中可以顯著提高效能,因為避免了在Java堆和Native中來回複製資料。
  • 本機的直接記憶體不會受到java堆記憶體大小的限制,但是伺服器管理員在配置虛擬機器引數時,會根據實際記憶體設定-Xmx等引數資訊,忽略了直接記憶體也會佔用一部分記憶體空間,使得各個記憶體區域的總和大於實體記憶體限制(包括物理的和作業系統層級的限制),從而導致動態擴充套件時出現OutOfMemoryError異常。