
1.?堆(Heap)
? ? ? ?是java虛擬機所管理的內存中最大的一塊內存區(qū)域,也是被各個線程共享的內存區(qū)域,該內存區(qū)域存放了對象實例及數(shù)組(但不是所有的對象實例都在堆中)。其大小通過-Xms(最小值)和-Xmx(最大值)參數(shù)設置(最大最小值都要小于1G),前者為啟動時申請的最小內存,默認為操作系統(tǒng)物理內存的1/64,后者為JVM可申請的最大內存,默認為物理內存的1/4,默認當空余堆內存小于40%時,JVM會增大堆內存到-Xmx指定的大小,可通過-XX:MinHeapFreeRation=來指定這個比列;當空余堆內存大于70%時,JVM會減小堆內存的大小到-Xms指定的大小,可通過XX:MaxHeapFreeRation=來指定這個比列,當然為了避免在運行時頻繁調整Heap的大小,通常-Xms與-Xmx的值設成一樣。堆內存 = 新生代+老生代+持久代。在我們垃圾回收的時候,我們往往將堆內存分成新生代和老生代(大小比例1:2),新生代中由Eden和Survivor0,Survivor1組成,三者的比例是8:1:1,新生代的回收機制采用復制算法,在Minor GC的時候,我們都留一個存活區(qū)用來存放存活的對象,真正進行的區(qū)域是Eden+其中一個存活區(qū),當我們的對象時長超過一定年齡時(默認15,可以通過參數(shù)設置),將會把對象放入老生代,當然大的對象會直接進入老生代。老生代采用的回收算法是標記整理算法。(更詳細的內容將后續(xù)文章詳細介紹GC回收)
2. 方法區(qū)(Method Area)
? ? ?方法區(qū)也稱"永久代",它用于存儲虛擬機加載的類信息、常量、靜態(tài)變量、是各個線程共享的內存區(qū)域。默認最小值為16MB,最大值為64MB(64位JVM由于指針膨脹,默認是85M),可以通過-XX:PermSize 和 -XX:MaxPermSize 參數(shù)限制方法區(qū)的大小。它是一片連續(xù)的堆空間,永久代的垃圾收集是和老年代(old generation)捆綁在一起的,因此無論誰滿了,都會觸發(fā)永久代和老年代的垃圾收集。不過,一個明顯的問題是,當JVM加載的類信息容量超過了參數(shù)-XX:MaxPermSize設定的值時,應用將會報OOM的錯誤。參數(shù)是通過-XX:PermSize和-XX:MaxPermSize來設定的
? ? ?運行時常量池(Runtime Constant Pool):是方法區(qū)的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯器生成的各種符號引用,這部分內容將在類加載后放到方法區(qū)的運行時常量池中。
? ? ?從JDK7開始移除永久代(但并沒有移除,還是存在),貯存在永久代的一部分數(shù)據(jù)已經轉移到了Java Heap或者是Native Heap:符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態(tài)變量(class statics)轉移到了java heap。從JDK8開始使用元空間(Metaspace),元空間的大小受本地內存限制,新參數(shù)(MaxMetaspaceSize)用于限制本地內存分配給類元數(shù)據(jù)的大小。如果沒有指定這個參數(shù),元空間會在運行時根據(jù)需要動態(tài)調整。
?具體的可以查看這篇文章:https://blog.csdn.net/zhushuai1221/article/details/52122880 (Java 8: 從永久代(PermGen)到元空間(Metaspace))
3.虛擬機棧(JVM Stack)
? ? ?描述的是java方法執(zhí)行的內存模型:每個方法被執(zhí)行的時候都會創(chuàng)建一個"棧幀",用于存儲局部變量表(包括參數(shù))、操作棧、方法出口等信息。每個方法被調用到執(zhí)行完的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。聲明周期與線程相同,是線程私有的。棧幀由三部分組成:局部變量區(qū)、操作數(shù)棧、幀數(shù)據(jù)區(qū)。局部變量區(qū)被組織為以一個字長為單位、從0開始計數(shù)的數(shù)組,和局部變量區(qū)一樣,操作數(shù)棧也被組織成一個以字長為單位的數(shù)組。但和前者不同的是,它不是通過索引來訪問的,而是通過入棧和出棧來訪問的,可以看作為臨時數(shù)據(jù)的存儲區(qū)域。除了局部變量區(qū)和操作數(shù)棧外,java棧幀還需要一些數(shù)據(jù)來支持常量池解析、正常方法返回以及異常派發(fā)機制。這些數(shù)據(jù)都保存在java棧幀的幀數(shù)據(jù)區(qū)中。
? ? 局部變量表:?存放了編譯器可知的各種基本數(shù)據(jù)類型、對象引用(引用指針,并非對象本身),其中64位長度的long和double類型的數(shù)據(jù)會占用2個局部變量的空間,其余數(shù)據(jù)類型只占1個。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量是完全確定的,在運行期間棧幀不會改變局部變量表的大小空間。
4.本地方法棧(Native Stack)
? ? ? ?與虛擬機?;绢愃?,區(qū)別在于虛擬機棧為虛擬機執(zhí)行的java方法服務,而本地方法棧則是為Native方法服務。(棧的空間大小遠遠小于堆)
5.程序計數(shù)器(PC Register)
? ?是最小的一塊內存區(qū)域,它的作用是當前線程所執(zhí)行的字節(jié)碼的行號指示器,在虛擬機的模型里,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、異常處理、線程恢復等基礎功能都需要依賴計數(shù)器完成。
6.直接內存
? ? 直接內存并不是虛擬機內存的一部分,也不是Java虛擬機規(guī)范中定義的內存區(qū)域。jdk1.4中新加入的NIO,引入了通道與緩沖區(qū)的IO方式,它可以調用Native方法直接分配堆外內存,這個堆外內存就是本機內存,不會影響到堆內存的大小.
參考:
http://www.hollischuang.com/archives/80
https://iamjohnnyzhuang.github.io/java/2016/07/12/Java%E5%A0%86%E5%92%8C%E6%A0%88%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F.html
https://www.zhihu.com/question/29833675
https://mritd.me/2016/03/22/Java-%E5%86%85%E5%AD%98%E4%B9%8B%E6%96%B9%E6%B3%95%E5%8C%BA%E5%92%8C%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0/
http://www.itdecent.cn/p/50be08b54bee
https://blog.csdn.net/zhushuai1221/article/details/52122880?