jvm記憶體模型可以分為 堆、方法區、虛拟機棧、本地方法棧、程式計數器五個區域。見下圖。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1QTNzUTN0MTM3IDNwYTMwIzLcRXZu5ibkN3Yuc2bsJmLn1Wavw1LcpDc0RHaiojIsJye.jpg)
方法區是各個線程共享的記憶體區域,用于存儲類的資訊、常量與靜态變量。
hotspot vm把方法區也稱為永久代,permanent space。永久代對垃圾回收沒有顯著影響。
-xx:permsize //永久代最小空間
-xx:maxpermsize //永久代最大空間
示例: -xx:permsize=100m -xx:maxpermsize
java.lang.outofmemoryerror:perm space
永久代記憶體溢出,調大記憶體即可。
堆通常是jvm記憶體中最大的一塊,用于盛放對象。
堆又可以分為新生代與老年代,見圖2-1.
圖2-1 jvm堆與方法區的記憶體模型
permanent space為永久代空間;heap space為堆空間,它又分為新生代空間與老年代空間。
它又可以分為2部分——eden space與survivor spaces,後者包括s0與s1。
eden ['i:dən]
n. 伊甸園(《聖經》中亞當和夏娃最初居住的地方)
新建立的對象将放入eden space。
又可以分為 s0 (survivor 0) 與 s1(survivor 1)。
當eden區滿時,觸發一次minor gc。還存活的對象将被複制到s0,同時清空eden區域。
當eden區再次滿時,觸發一次minor gc。eden+s0(不管s0有沒有滿)還存活的對象将被複制到s1,同時清空eden+s0區域。
當eden區再次滿時,觸發一次minor gc。eden+s1(不管s1有沒有滿)還存活的對象将被複制到s0,同時清空eden+s1區域。
如此往複。。。
注意survivor from 與survivor to的概念和s0與s1的概念。
新生代采用的就是複制算法。當survivor from 記憶體用完,就将還活着的對象複制到另外一塊survivor to上面。複制算法不會産生記憶體碎片。
新生代gc也是需要stop the world的,通常為幾十毫秒。
在新生代中經曆了n次垃圾回收後仍然存活的對象,就會被放到老年代中。是以可以認為老年代中存放的都是一些生命周期較長的對象。
一個對象,每經曆一次gc(minor gc 或 major gc),年齡就加一歲。
-xx:maxtenuringthreshold=15
指定對象到達15歲時被移到old區。預設值為15。
需要注意的是,并不是年齡非得到達指定值後才會被移到老年代,jvm還有自己的一套規則,煩人。
-xx:+printtenuringdistribution
這個參數用于顯示每次minor gc時survivor區中各個年齡段的對象的大小。
-xmx
//最大堆記憶體
-xms
//最小堆記憶體
-xmn
//新生代記憶體
示例:-xms2048m -xmx5120m
-xx:survivorratio//新生代中,eden區域與survivor區域的比值。預設為8。
java.lang.outofmemoryerror:java heap space
堆溢出。
名為pc,program counter,是一個寄存器,存放該線程要執行的下一條語句的位置。
每個線程都有自己的pc,線程間不共享。
每個線程都有自己的java虛拟機棧,線程間不共享。
每當進入一個函數時,建立棧幀(即stack frame,内含局部變量與函數出口等資訊),入棧,函數執行完退出時,出棧。
-xss
//每個線程的棧大小,預設為1m
示例: -xss 2m
java.lang.stackoverflowerror
棧溢出。預設情況下函數嵌套達到2000層是沒有問題的。可以認為調大棧空間。
每個線程都有自己的本地方法棧,線程間不共享。
與java虛拟機棧類似,服務于本地(native)代碼。