Java虚拟机内存模型初步学习
Java虛擬機—Java8內存模型
1.概述
? ? ? ?對于Java程序員來說,在虛擬機自動內存管理機制的幫助下,不再需要手動釋放內存,不容易出現內存泄露和內存溢出問題。一旦出現內存泄露和溢出方面的問題,如果不了解虛擬機是怎樣使用內存的,排查錯誤將會異常艱難。
2. 運行時數據區域
? ? ? ?Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若干個不同的數據區域。這些區域都有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而存在,有些區域則依賴用戶線程的啟動和結束而建立和銷毀。Java虛擬機hotspot所管理的內存包括以下幾個運行時數據區域,如圖所示:
? ? ?
?2.1 程序計數器
? ? ? ?程序計數器(Program Counter Register)是一塊較小的內存空間,它可以看作是當前線程所執行的Java字節碼的行號指示器。
? ? 每條線程都需要有一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,我們稱這類內存區域為“線程私有”的內存。
?2.2 Java虛擬機棧
與程序計數器一樣,Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關位置)和returnAddress類型(指向了一條字節碼指令的地址)。
?2.3 本地方法棧
本地方法棧(Native Method Stack)與虛擬機棧所發揮的作用是非常相似的,它們之間的區別不過是虛擬機棧為虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則為虛擬機使用到的Native方法服務。
參數設置:
-Xss 設置每個線程的棧大小。JDK1.5+ 每個線程棧大小為1M,一般來說如果棧不是很深的話,1M是絕對夠用的啦。
參數含義解析:
以-X開頭的參數是和實現有關的,第一個s表示stack,第二個s表示size;
注意:
在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右。
?2.4 Java堆
對于大多數應用來說,Java堆(Java Heap)是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。
Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱做“GC堆”(Garbage Collected Heap)。
根據Java虛擬機規范的規定,Java堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可,就像我們的磁盤空間一樣。在實現時,既可以實現成固定大小的,也可以是擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(通過-Xmx和-Xms控制)。如果在堆中沒有內存完成實例分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError異常。
參數設置:
-Xms 設置堆的最小空間大小;通常為操作系統可用內存的1/64大小即可。
-Xmx 設置堆的最大空間大小;通常為操作系統可用內存的1/4大小。
-Xmn 設置新生代大小,是對-XX:newSize、-XX:MaxnewSize兩個參數的同時配置,這個參數是在JDK1.4版本以后出現的;通常為Xmx的1/3或1/4。新生代 = Eden + 2個Survivor空間。實際可用空間 = 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。
?2.5 方法區(永久代)
方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,它用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,即存放靜態文件,如Java類、方法等。
根據Java虛擬機規范的規定,當方法區無法滿足內存分配需求時,將拋出OutOfMemoryError異常。
運行時常量池和靜態變量都存儲到了堆中,MetaSpace存儲類的元數據,MetaSpace直接在本地內存中(Native memory),這樣類的元數據分配只受本地內存大小的限制,OOM問題就不存在了。
?參數設置:
-XX:PermSize設置永久代最小空間大小;
-XX:MaxPermSize設置永久代最大空間大小;
2.5.1 運行時常量池
運行時常量池(Runtime Constant Pool)是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table),用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。
既然運行時常量池是方法區的一部分,自然受到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError異常。
2.6 直接內存(堆外內存)
直接內存(Direct Memory),也叫堆外內存,它并不是虛擬機運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域,而是Java虛擬機的堆以外的內存,直接受操作系統管理。
在JDK1.4中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外內存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。
? 參數設置:可以通過 -XX:MaxDirectMemorySize參數來設置最大可用直接內存,如果Java虛擬機啟動時未設置則默認為最大堆內存大小,即與 -Xmx相同。即假如最大堆內存為1G,則默認直接內存也為1G,那么JVM最大需要的內存大小為2G多一些。當直接內存達到最大限制時就會觸發GC,如果回收失敗則會引起OutOfMemoryError。
總結
以上是生活随笔為你收集整理的Java虚拟机内存模型初步学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java虚拟机字节码指令概述
- 下一篇: java美元兑换,(Java实现) 美元