首先列出內存結構
靜態區
- permspace(jdk<=1.7)
`線程共享,存儲常量、靜態變量、加載的類信息,由參數指定大小
- metaspace(jdk>=1.8)
線程共享,替代permspace,常量信息移動到堆當中,虛擬機維護
棧區
- JAVA棧
線程私有,當前調用棧的上下文、localcar、方法的成員變量等
- 本地方法棧(native)
線程私有,虛擬機或jni提供的本地方法的調用上下文
- 程序計數器
線程私有,存儲當前線程行號、分支、循環、跳轉、異常信息、同時存儲線程狀態切換等信息
堆區
- JAVA堆
共享,存儲java對象,等,分爲多個空間
非堆區
- 非堆
程序自己控制的內存區域,可以由NIO的DiretByteBuffer或UNSAFE方法分配
JAVA垃圾回收原理
JAVA中由垃圾回收器負責的空間主要爲堆與靜態區,靜態區在老FGC時均會被整理,且其也會觸發FGC不再闡述
-
堆的結構
-
堆分爲兩個部分,新生代與老年代
-
- 新生代:JAVA對象在創建之初都存在於新生代(大對象除外),經過多次GC仍然存在的對象會被移動到老年代,由空間的連續性考慮,新生代的GC比較頻繁,使用只複製法整理空間效率更高,但消耗更多的內存。所以堆在內部又再次劃分爲兩個空間類型,Eden與Survivor,其中Survior內部再次劃分爲兩個空間且一個時間只會使用其中一個,兩個Survior空間在每次GC時交換身份。當GC被觸發時,Eden中存活下來的對象將會移動到正在使用的Survivor當中,當此Survivor滿時,則對其進行整理,仍然存活的對象會被複制到另外一個Survivor當中。
-
- 老年代:一個對象在Survivor中多次存活(次數可配),則會被移動到老年代。考慮到空間的使用,與被觸發GC次數較少,使用內部整理的方式來處理連續性,降低了內存開銷與複雜度。
-
堆的垃圾收集算法
- 1)複製算法:*只適用於新生代。內存拆分爲兩份,申請內存時只返回正在使用的那部分,這個空間稱作Eden,另外一個稱爲Survivor,標記回收的對象後,將不需要回收的對象複製到Survivor空間。算法實現有:Serial(單線程);ParNew(多線程收集器);ParallelScavenge(吞吐量優化算法,自動調節策略降低停頓)
- 2)標記清除算法:*只適用於老年代。首先根據引用計數標記需要回收的對象,隨後統一回收。會產生較多碎片,但性能最高。算法實現有:ConcurrentMarkSweep(CMS,併發清除算法,分三階段執行,僅前兩個階段停頓,停頓最小,產生的碎片智能整理(需要時),也可以配置整理)
- 3)標記整理算法:*只適用於老年代,申請內存時,只採用一個方向的分配方式,則內存的另一端將不會被使用。之後在將不需要回收的對象向此端複製。算法實現有:SerialOld(單線程),ParallelOld(多線程)
- 4)分區整理算法:新型的內存結構,不再適用上文中的內存結構,它將JAVA堆內存分割爲多個區域,每個區域都可以成爲新生代或老年代的身份,當空間不足時便對這些分區分個進行整理,存活的對象複製到新的分區中,這樣每次GC都不用對整個空間做整理,停頓非常少。算法實現:GarbageFirst(G1)
GC分析
JAVA當中的GC分爲兩種,Minor GC / Major GC(FGC)
…待整理
內存使用優化思路
-
合理使用非堆
- 非堆的性能優化點在於不在垃圾回收器的工作內容之中,合理使用降低垃圾回收器的負擔,同時,非堆也優化了IO的操作,減少JAVA虛擬機內存向系統內存拷貝的環節,優化性能
-
集合的初始化大小
- 儘量指定集合的初始化大小,降低擴容次數
待整理。。。。。
GC優化思路
待整理。。。。。