<form id="bhp1t"></form>

<em id="bhp1t"></em>

    <form id="bhp1t"></form>

      Java虛擬機的運行時數據區域及內存的管理方法

      電腦雜談  發布時間:2021-06-12 06:03:20  來源:網絡整理

      1.概覽

      對于Java程序員來說,借助虛擬機的自動內存管理機制,不再需要手動釋放內存,也不容易出現內存泄漏和內存溢出問題。一旦發生內存泄漏和溢出,如果您不了解虛擬機是如何使用內存的,那么排查錯誤將極其困難。

      2.運行時數據區

      Java 虛擬機在 Java 程序執行過程中將其管理的內存劃分為幾個不同的數據區。這些地區有自己的目的,也有創造和毀滅的時間。有些區域隨著虛擬機進程的啟動而存在,有些區域的創建和銷毀取決于用戶線程的開始和結束。 Java虛擬機熱點管理的內存包括以下運行時數據區,如圖:

      java虛擬機內存模型

      2.1 程序計數器

      程序計數器寄存器是一個很小的內存空間,可以看作是當前線程執行的Java字節碼的行號指示器。在虛擬機的概念模型中,當字節碼解釋器工作時,它通過改變這個計數器的值來選擇下一條要執行的字節碼指令。分支、循環、跳轉、異常處理、線程恢復等基本功能都需要依賴這個計數器來完成。

      因為Java虛擬機的多線程是通過線程交替切換和分配處理器執行時間的方式來實現的,所以在某一時刻,一個處理器只會執行一個線程中的指令。因此,為了程切換后恢復到正確的執行位置,每個線程都需要有一個獨立的程序計數器。線程之間的計數器互不影響,獨立存儲。我們稱這種類型的內存區域為“線程私有”內存。

      如果線程正在執行一個Java方法,這個計數器記錄了正在執行的虛擬機字節碼指令的地址;如果它正在執行 Native 方法,則計數器值為空(未定義)。此內存區域是 Java 虛擬機規范中唯一未指定 OutOfMemoryError 的區域。

      2.2Java 虛擬機棧

      和程序計數器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程的生命周期相同。虛擬機棧描述了Java方法執行的內存模型:每個方法執行時,都會創建一個Stack Frame來存儲局部變量表、操作數棧、動態鏈接、方法退出等信息。每個方法從調用到執行完成的過程,對應著虛擬機棧中一個棧幀入棧到出棧的過程。

      有些人經常將Java內存分為堆內存(Heap)和棧內存(Stack)。所指的“堆”是Java堆,所指的“棧”是虛擬機棧,或者說是虛擬機棧中局部變量表的一部分。

      局部變量表存儲了編譯時已知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(引用類型,它不等同于對象本身,它可能是一個指向對象起始地址的引用指針,也可能指向一個表示該對象的句柄或與該對象相關的其他位置)和 returnAddress 類型(指向一個字節碼指令的地址)。

      64位長的long和double數據會占用兩個局部變量空間(Slot),其余數據類型只占用一個。局部變量表所需的內存空間在編譯時分配。進入一個方法時,該方法需要在幀中分配多少局部變量空間是完全確定的,并且在方法運行過程中不會改變局部變量表的大小。

      在Java虛擬機規范中,對這個區域規定了兩種異常情況:如果線程請求的棧深度大于虛擬機允許的深度,則拋出StackOverflowError異常;如果虛擬機棧可以動態擴展(目前很大一部分Java虛擬機可以動態擴展,但是Java虛擬機規范也允許固定長度的虛擬機棧)。如果擴容時無法申請足夠的內存,則會拋出 OutOfMemoryError 異常。

      java虛擬機內存模型_java內存模型 堆棧_java內存模型 和堆棧的區別

      2.3 本地方法棧

      Native Method Stack 和虛擬機堆棧所扮演的角色非常相似。它們的區別在于虛擬機棧為虛擬機執行Java方法(即字節碼)服務,而native方法棧為虛擬機使用的Native方法服務。在虛擬機規范中,本地方法棧中的方法所使用的語言、用法和數據結構不是強制性的。 HotSpot 虛擬機直接將本地方法棧和虛擬機棧合二為一。和虛擬機棧一樣,本地方法棧區也會拋出 StackOverflowError 和 OutOfMemoryError 異常。

      參數設置:

      -Xss 設置每個線程的堆棧大小。 JDK1.5+每個線程的棧大小為1M。一般來說,如果棧不是很深,1M絕對夠了。

      參數含義解析:

      注意:

      在相同的物理內存下,減小這個值可以產生更多的線程。但是操作系統對進程中的線程數還是有限制的,不能無限生成。經驗值在3000~5000左右。

      2.4Java 堆

      對于大多數應用程序來說,Java 堆是 Java 虛擬機管理的最大一塊內存。 Java堆是所有線程共享的內存區域,在虛擬機啟動時創建。這個內存區的唯一用途就是存放對象實例,幾乎所有的對象實例都在這里分配內存。這在Java虛擬機規范中是這樣描述的:所有的對象實例和數組都必須分配在堆上,但是隨著JIT編譯器的發展和逃逸分析技術的逐漸成熟,棧分配和標量替換優化技術將作為結果,發生了一些微妙的變化,所有對象都分配在堆上,逐漸變得不那么“絕對”。

      Java 堆是垃圾收集器管理的主要區域,因此常被稱為“GC 堆”(Garbage Collected Heap)。從內存回收的角度來看,由于現在收集器基本上都是采用分代收集算法,所以Java堆也可以細分為:新生代和老年代;更詳細一點,新生代可以有Eden空間和From Survivor Space、To Survivor空間等。 從內存分配的角度來看,線程共享的Java堆可能會被劃分為多個線程私有的分配緩沖區(Thread Local Allocation Buffer,表)。但是,無論劃分如何,都與存儲內容無關。無論哪個區域,存儲的仍然是對象實例。進一步劃分的目的是為了更好地回收內存或更快地分配內存。

      根據Java虛擬機規范,Java堆可以在物理上不連續的內存空間,只要在邏輯上是連續的,就像我們的磁盤空間一樣。在實現上,可以實現為固定大小,也可以擴展實現,但是目前主流的虛擬機都是按照可擴展性來實現的(由-Xmx和-Xms控制)。如果堆中沒有內存來完成實例分配,并且無法再擴展堆,則會拋出 OutOfMemoryError 異常。

      參數設置:

      -Xms 設置堆的最小大小;通常是操作系統可用內存的 1/64。 -Xmx 設置堆的最大大小;通常是操作系統可用內存的 1/4。 -Xmn 設置新生代大小,同時配置-XX:newSize和-XX:MaxnewSize這兩個參數。該參數出現在JDK1.4版本之后;通常是 Xmx 的 1/3 或 1/4。新生代 = 伊甸園 + 2 個幸存者空間。實際可用空間 = Eden + 1 Survivor,即 90%。 -XX:NewSize 設置新生代的最小空間大小; -XX: MaxNewSize 設置新生代的最大空間大小; -XX:NewRatio 新生代與老年代的比例,如-XX:NewRatio=2,新生代占整個堆空間的1/3,老年代占2/3。 -XX: SurvivorRatio 新生代中Eden與Survivor的比例。默認值為8,即Eden占據新生代空間的8/10,另外兩個Survivor各占1/10。

      參數含義解析:

      java虛擬機內存模型_java內存模型 堆棧_java內存模型 和堆棧的區別

      注意:

      在開發過程中,-Xms和-Xmx這兩個參數通常配置為相同的值。目的是為了在Java垃圾回收機制清理完堆區后,能夠浪費對堆區大小的計算。資源。

      2.5 方法區(永久代)

      Method Area 和 Java 堆一樣,是每個線程共享的內存區域。它用于存儲虛擬機已經加載的類信息、常量、靜態變量和即時編譯器編譯的代碼等數據。即存放靜態文件,如Java類、方法等。雖然Java虛擬機規范將方法區描述為堆的邏輯部分,但它有一個別名叫做Non-Heap(非堆),目的應該是和Java堆區分開來的。

      對于習慣于在 HotSpot 虛擬機上開發和部署程序的開發者來說,很更喜歡稱這種方式為“永久代”。本質上,兩者并不等價,只是因為HotSpot虛擬機設計團隊選擇將GC分代收集擴展到方法區,或者使用永久代來實現方法區,所以HotSpot的垃圾收集器可以管理這部分內存像Java堆一樣,可以節省專門為方法區編寫內存管理代碼的工作。

      根據Java虛擬機規范,當方法區不能滿足內存分配要求時,會拋出OutOfMemoryError異常。

      方法區在不同的虛擬機中有不同的實現。 HotSpot 在1.7 版本、1.7 版本和1.7 版本之前發生了變化。

      ①jdk7版本之前的實現如下圖所示:

      java虛擬機內存模型

      ②目前已經發布的JDK1.7的HotSpot中,原本放在永久代中的字符串常量池已經移到了Java堆中。

      ③在jdk8版本中,永久代徹底刪除,換成MetaSpace,如圖:

      java虛擬機內存模型

      常量池和靜態變量在運行時存儲在堆中。 MetaSpace 存儲類的元數據。 MetaSpace 直接存儲在本地內存(Native memory)中。此類元數據的分配僅受本地內存大小的限制。 OOM 問題不再存在。

      參數設置:

      java內存模型 和堆棧的區別_java內存模型 堆棧_java虛擬機內存模型

      -XX:PermSize 設置永久代的最小空間大小; -XX: MaxPermSize 設置永久代的最大空間大小;

      參數含義解析:

      注意:

      2.5.1 運行時常量池

      運行時常量池是方法區的一部分。 Class文件中除了類版本、字段、方法、接口的描述信息外,還有一個常量池(Constant Pool Table),用于存儲編譯時產生的各種文字和符號引用。這部分內容會在類加載后保存在方法區的運行時常量池中。

      Java 虛擬機對 Class 文件的各個部分(自然包括常量池)的格式有嚴格的規定。每個字節用來存儲什么樣的數據,必須滿足規范的要求,才能被虛擬機識別并加載和執行,但是對于運行時常量池,Java虛擬機規范并沒有做任何詳細的要求。不過一般情況下,除了保存Class文件中描述的符號引用外,翻譯后的直接引用也會保存在運行時常量池中。

      與類文件常量池相比,運行時常量池的另一個重要特性是它是動態的。 Java 語言并不要求常量只在編譯時生成,即不會在 Class 文件中預置到常量池中。在方法區進入運行時常量池,你也可以在運行時將新的常量放入池中。這個特性是開發者使用比較頻繁的String類的intern()方法。

      由于運行時常量池是方法區的一部分,自然受到方法區內存的限制。當常量池不能再申請內存時,會拋出 OutOfMemoryError 異常。

      2.6 直接內存(堆外內存)

      直接內存(Direct Memory),也叫堆外內存,不是虛擬機運行時的數據區的一部分,也不是Java虛擬機規范中定義的內存區,而是Java虛擬機堆外的內存,由操作系統直接管理。但是這部分內存也是經常使用的,也可能會導致出現OutOfMemoryError。使用堆外內存有兩個優點。一是減少垃圾收集,二是提高復制速度。例如,NIO 使用堆外內存。可以使用 NIO 包下未公開的 Unsafe 和 ByteBuffer 來創建堆外內存。

      在JDK1.4中,新增了NIO(New Input/Output)類,并引入了基于通道和緩沖區的I/O方法。它可以直接使用Native函數庫分配堆外內存,然后使用Java堆中存儲的一個DirectByteBuffer對象作為對這塊內存的引用進行操作。這在某些情況下可以顯著提高性能,因為它避免了在 Java 堆和 Native 堆之間來回復制數據。

      顯然,機器的直接內存分配不會受到Java堆大小的限制,但是既然是內存,肯定會受到機器總內存大小(包括RAM和SWAP區或分頁文件)和處理尋址空間限制。服務器管理員在配置虛擬機參數時,會根據實際內存設置-Xmx等參數信息,但往往會忽略直接內存,使得每個內存區域的總和大于物理內存限制(包括物理和操作系統級別的限制) ,導致動態擴容時出現 OutOfMemoryError 異常。

      參數設置:最大可用直接內存可以通過-XX:MaxDirectMemorySize參數設置。如果Java虛擬機啟動時沒有設置,默認是最大堆內存大小,與-Xmx相同。也就是說,如果最大堆內存是1G,那么默認的直接內存也是1G,所以JVM需要的最大內存大小是2G以上。當直接內存達到最大限制時,將觸發 GC。如果恢復失敗,會導致OutOfMemoryError。

      總結

      參考資料:

      《深入理解Java虛擬機JVM的高級特性和最佳實踐》第二版


      本文來自電腦雜談,轉載請注明本文網址:
      http://www.humishu.com/a/shoujiruanjian/article-382225-1.html

        相關閱讀
        發表評論  請自覺遵守互聯網相關的政策法規,嚴禁發布、暴力、反動的言論

        熱點圖片
        拼命載入中...
        黄色电影免费片日本大片 - 视频 - 在线观看 - 影视资讯 -心晴网