更新時間:2021-08-02 16:58:13 來源:動力節(jié)點 瀏覽1211次
Java堆內存管理是影響性能的主要因素之一。
堆內存溢出是Java項目非常常見的故障,在解決該問題之前,必須先了解下Java堆內存是怎么工作的。
先看下JAVA堆內存是如何劃分的,如圖:
在JDK1.8版本廢棄了永久代,替代的是元空間(MetaSpace),元空間與永久代上類似,都是方法區(qū)的實現(xiàn),他們最大區(qū)別是:元空間并不在JVM中,而是使用本地內存。
元空間有兩個參數(shù):
移除永久代原因:為融合HotSpot JVM與JRockit VM(新JVM技術)而做出的改變,因為JRockit沒有永久代。
有了元空間就不再會出現(xiàn)永久代OOM問題了
新生成的對象首先放到年輕代Eden區(qū),當Eden空間滿了,觸發(fā)Minor GC,存活下來的對象移動到Survivor0區(qū),Survivor0區(qū)滿后觸發(fā)執(zhí)行Minor GC,Survivor0區(qū)存活對象移動到Survivor1區(qū),這樣保證了一段時間內總有一個survivor區(qū)為空。經(jīng)過多次Minor GC仍然存活的對象移動到老年代。
老年代存儲長期存活的對象,占滿時會觸發(fā)Major GC=Full GC,GC期間會停止所有線程等待GC完成,所以對相應要求高的應用盡量減少發(fā)生Major GC,避免響應超時。
所有GC都會停止所有應用進程
將對象根據(jù)存活概率進行分類,對存活時間長的對象,放到固定區(qū),從而減少掃描垃圾時間及GC頻率。針對分類進行不同的垃圾回收算法,對算法揚長避短。
主要為了解決碎片化。如果內存碎片化嚴重,也就是兩個對象占用不連續(xù)的內存,已有的連續(xù)內存不夠新對象存放,就會觸發(fā)GC。
參數(shù) | 描述 |
-Xms | 堆內存初始大小,單位m、g |
-Xmx(MaxHeapSize) | 堆內存最大允許大小,一般不要大于物理內存的80% |
-XX:PermSize | 非堆內存初始大小,一般應用設置初始化200m,最大1024m就夠了 |
-XX:MaxPermSize | 非堆內存最大允許大小 |
-XX:NewSize(-Xns) | 年輕代內存初始大小 |
-XX:MaxNewSize(-Xmn) | 年輕代內存最大允許大小,也可以縮寫 |
-XX:SurvivorRatio=8 | 年輕代中Eden區(qū)與Survivor區(qū)的容量比例值,默認為8,即8:1 |
-Xss | 堆棧內存大小 |
紅色是標記的非活動對象,綠色是活動對象。
GC分為兩個階段,標記和清除。首先標記所有可回收的對象,在標記完成后統(tǒng)一回收所有被標記的對象。同時會產(chǎn)生不連續(xù)的內存碎片。碎片過多會導致以后程序運行時需要分配較大對象時,無法找到足夠的連續(xù)內存,而不得已再次觸發(fā)GC。
將內存按容量劃分為兩塊,每次只使用其中一塊。當這一塊內存用完了,就將存活的對象復制到另一塊上,然后再把已使用的內存空間一次清理掉。這樣使得每次都是對半個內存區(qū)回收,也不用考慮內存碎片問題,簡單高效。缺點需要兩倍的內存空間。
也分為兩個階段,首先標記可回收的對象,再將存活的對象都向一端移動,然后清理掉邊界以外的內存。此方法避免標記-清除算法的碎片問題,同時也避免了復制算法的空間問題。
一般年輕代中執(zhí)行GC后,會有少量的對象存活,就會選用復制算法,只要付出少量的存活對象復制成本就可以完成收集。
而老年代中因為對象存活率高,沒有額外過多內存空間分配,就需要使用標記-清理或者標記-整理算法來進行回收。
以上就是動力節(jié)點小編介紹的"JVM堆內存",希望對大家有幫助,想了解更多可查看Java虛擬機視頻。動力節(jié)點在線學習教程,針對沒有任何Java基礎的讀者學習,讓你從入門到精通,主要介紹了一些Java基礎的核心知識,讓同學們更好更方便的學習和了解Java編程,感興趣的同學可以關注一下。