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