二: Jvm內存模型
為每個對象生命周期不一樣,jvm在做內存管理的時候,就幫我們分成了三個區域:
1. 新生代(回收頻率高) 新生和老年默認大小比例為1:2
2. 老年代(回收頻率低) 最好所有的對象都不要進入老年代,最好新生代能及時回收空對象釋放空間供下次使用。
3. 永久代(一般放類的加載信息,常量,靜態變量)。
GC回收算法:
GC回收是采用的復制回收算法,默認將新生代內存分為8:1:1,達到90%的利用率。
創建的對象默認放在eden區中,eden區內存快滿的時候,就會觸發minor gc來回收空對象,將不能回收的對象放到s0區裏面;
當再次創建多個對象後 eden區又快滿了,這時又觸發minor gc回收eden和s0裏面的空對象,若esdn區和s0區內存不足,將剩余的放到s1區中;
如此反復新生代沒有空間時,對象就會放到老年代中。
從上圖可以看出jdk1.8之前:新生代,老年代放在heap中, 永久代放在方法區中;
在jdk1.8的時候,就將永久代放到了一塊叫Meta Space(元空間)的本地內存中。
官方之所以這麽設計,是為了解決永久代會溢出的問題,meta space有點像ArrayList,擁有自動擴容的特性,從而防止溢出。當然它也不是越大越好,太大了會因為內存占用過多,從而使得堆外內存空間狹小而容易出現內存溢出的情況。這些都是可配置的。
指針碰撞:
如下圖,第一次創建對象的時候線程開辟了一個空間,第二次創建對象線程所有開了了一個空間,如果多個線程同時創建就會出現“搶占”空間的情況出現指針碰撞,jvm就通過CAS來控制先來
後到的順利,理解成線程鎖一樣。但是這樣創建對象還是CAS還是會出現競爭激烈的情況從而消耗CPU影響性能。為了解決這個問題,jvm又提供了棧上分配。內存規整(即內存連續有規律)
棧上分配:
棧上分配的本質還是在堆中分配內存。
如下圖:在堆裏面,每個線程都有自己的Thread local Alltion Buffer,他們都是在自己的空間裏面創建對象,這樣就不會出現搶占的情況了,從而提高了性能。
對象分配規則:
(1)對象優先分配在Eden區,如果Eden內存不夠,虛擬機就執行一次Minor GC
(2)大對象(大對象指需要大量連續內存空間的對象)直接進入老年代.這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代采用復制算法收集內存)。
(3)長期存活的對象進入老年代.虛擬機為每個對象都定義一個年齡計數器,如果對象經過一次Minor GC就去Survivor區,之後每經過一次Minor GC,年齡就會加一,直到15之後就去老年代.
(4)動態判斷對象的年齡.如果Survivor區相同年齡的對象的總和大於Survivor的一半,那麽大於或者等於這個年齡的對象直接去老年代
二: Jvm內存模型