內(nèi)存優(yōu)化篇
- memory贡耽、GC冗恨、Performance
GC(Garbage Collection):垃圾回收是jvm提供的一種垃圾回收機制增显,回收的是無任何對象引用指向的內(nèi)存空間时鸵。垃圾回收釋放的是對象占據(jù)的內(nèi)存(一般為堆內(nèi)存)斋日;一般在程序空閑時間不定時回收。
java中的對象引用:
強引用(Strong Reference):如Object obj = new Object()挠羔,只要對象還存在井仰,對象引用的地址就永遠不會被回收。
軟引用(Soft Reference):定義的對象為非必需的破加。在系統(tǒng)內(nèi)存不夠用時俱恶,軟引用關(guān)聯(lián)的對象被垃圾收集器回收。JDK1.2之后提供SoftReference類來實現(xiàn)軟引用范舀。
弱引用(Weak Reference):定義的對象是非必需的合是,強度比軟引用弱,弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前锭环。當垃圾收集器工作時聪全,無論當前內(nèi)存是否足夠,都會回收弱引用對象引用的內(nèi)存辅辩。在JDK1.2之后难礼,提供WeakReference類來實現(xiàn)弱引用。
虛引用(Phantom Reference):最弱的一種引用關(guān)系玫锋,完全不會對其生存時間構(gòu)成影響蛾茉,也無法通過虛引用來取得一個對象實例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的是希望能在這個對象被收集器回收時收到一個系統(tǒng)通知撩鹿。JDK1.2之后提供PhantomReference類來實現(xiàn)虛引用谦炬。
檢索回收的對象
jvm會查詢所有未銷毀的對象->回收垃圾對象占用的內(nèi)存。垃圾對象的確認(引用計數(shù))
其實不管是Object-C還是java,都是通過一個引用計數(shù)的原理來跟蹤一個對象的使用情況键思,每個對象從被創(chuàng)建那一時刻起就會有一個引用計數(shù)器伴其一生础爬。
- 對象被初始化時計數(shù)器值為1 -> 被另一個對象引用時加1 -> 引用的對象為null時,減1 -> 計數(shù)為0時jvm視其為垃圾吼鳞。
優(yōu)點:引用計數(shù)收集器執(zhí)行簡單看蚜,判定效率高,交織在程序運行中赖条。對程序不被長時間打斷的實時環(huán)境比較有利(OC的內(nèi)存管理使用該算法)失乾。
缺點: 難以檢測出對象之間的循環(huán)引用常熙。同時纬乍,引用計數(shù)器增加了程序執(zhí)行的開銷。所以Java語言并沒有選擇這種算法進行垃圾回收裸卫。
早期的JVM使用引用計數(shù)仿贬,現(xiàn)在大多數(shù)JVM采用對象引用遍歷(根搜索算法)。
Java的堆內(nèi)存(Java Heap Memory)
Java的堆內(nèi)存基于Generation算法(Generational Collector)劃分為新生代墓贿、年老代和持久代茧泪。新生代又被進一步劃分為Eden和Survivor區(qū),最后Survivor由FromSpace(Survivor0)和ToSpace(Survivor1)組成聋袋。所有通過new創(chuàng)建的對象的內(nèi)存都在堆中分配队伟,其大小可以通過-Xmx和-Xms來控制。
- 堆內(nèi)存分配區(qū)域:
1.年輕代(Young Generation)
幾乎所有新生成的對象首先都是放在年輕代的幽勒。新生代內(nèi)存按照8:1:1的比例分為一個Eden區(qū)和兩個Survivor(Survivor0,Survivor1)區(qū)嗜侮。大部分對象在Eden區(qū)中生成。當新對象生成啥容,Eden Space申請失斝饪拧(因為空間不足等),則會發(fā)起一次GC(Scavenge GC)咪惠』髦ǎ回收時先將Eden區(qū)存活對象復制到一個Survivor0區(qū),然后清空Eden區(qū)遥昧,當這個Survivor0區(qū)也存放滿了時覆醇,則將Eden區(qū)和Survivor0區(qū)存活對象復制到另一個Survivor1區(qū),然后清空Eden和這個Survivor0區(qū)炭臭,此時Survivor0區(qū)是空的叫乌,然后將Survivor0區(qū)和Survivor1區(qū)交換,即保持Survivor1區(qū)為空徽缚, 如此往復憨奸。當Survivor1區(qū)不足以存放 Eden和Survivor0的存活對象時,就將存活對象直接存放到老年代凿试。當對象在Survivor區(qū)躲過一次GC的話排宰,其對象年齡便會加1似芝,默認情況下,如果對象年齡達到15歲板甘,就會移動到老年代中党瓮。若是老年代也滿了就會觸發(fā)一次Full GC,也就是新生代盐类、老年代都進行回收寞奸。新生代大小可以由-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例在跳。
2.年老代(Old Generation)
在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對象枪萄,就會被放到年老代中。因此猫妙,可以認為年老代中存放的都是一些生命周期較長的對象瓷翻。內(nèi)存比新生代也大很多(大概比例是1:2),當老年代內(nèi)存滿時觸發(fā)Major GC即Full GC割坠,F(xiàn)ull GC發(fā)生頻率比較低齐帚,老年代對象存活時間比較長,存活率標記高彼哼。一般來說对妄,大對象會被直接分配到老年代。所謂的大對象是指需要大量連續(xù)存儲空間的對象敢朱,最常見的一種大對象就是大數(shù)組剪菱。比如:
byte[] data = new byte[410241024]
這種一般會直接在老年代分配存儲空間。
當然分配的規(guī)則并不是百分之百固定的蔫饰,這要取決于當前使用的是哪種垃圾收集器組合和JVM的相關(guān)參數(shù)琅豆。
3.持久代(Permanent Generation)
用于存放靜態(tài)文件(class類、方法)和常量等篓吁。持久代對垃圾回收沒有顯著影響茫因,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate 等杖剪,在這種時候需要設(shè)置一個比較大的持久代空間來存放這些運行過程中新增的類冻押。對永久代的回收主要回收兩部分內(nèi)容:廢棄常量和無用的類。
永久代空間在Java SE8特性中已經(jīng)被移除盛嘿。取而代之的是元空間(MetaSpace)洛巢。因此不會再出現(xiàn)“java.lang.OutOfMemoryError: PermGen error”錯誤。
- 堆內(nèi)存分配策略明確以下三點:
(1)對象優(yōu)先在Eden分配次兆。
(2)大對象直接進入老年代稿茉。
(3)長期存活的對象將進入老年代。
- 對垃圾回收機制說明以下三點:
新生代GC(Minor GC/Scavenge GC):發(fā)生在新生代的垃圾收集動作。因為Java對象大多都具有朝生夕滅的特性漓库,因此Minor GC非常頻繁(不一定等Eden區(qū)滿了才觸發(fā))恃慧,一般回收速度也比較快。在新生代中渺蒿,每次垃圾收集時都會發(fā)現(xiàn)有大量對象死去痢士,只有少量存活,因此可選用復制算法來完成收集茂装。
老年代GC(Major GC/Full GC):發(fā)生在老年代的垃圾回收動作怠蹂。Major GC,經(jīng)常會伴隨至少一次Minor GC少态。由于老年代中的對象生命周期比較長城侧,因此Major GC并不頻繁,一般都是等待老年代滿了后才進行Full GC况增,而且其速度一般會比Minor GC慢10倍以上赞庶。另外训挡,如果分配了Direct Memory澳骤,在老年代中進行Full GC時,會順便清理掉Direct Memory中的廢棄對象澜薄。而老年代中因為對象存活率高为肮、沒有額外空間對它進行分配擔保,就必須使用標記—清除算法或標記—整理算法來進行回收肤京。
新生代采用空閑指針的方式來控制GC觸發(fā)颊艳,指針保持最后一個分配的對象在新生代區(qū)間的位置,當有新的對象要分配內(nèi)存時忘分,用于檢查空間是否足夠棋枕,不夠就觸發(fā)GC。當連續(xù)分配對象時妒峦,對象會逐漸從Eden到Survivor重斑,最后到老年代。
用Java VisualVM來查看肯骇,能明顯觀察到新生代滿了后窥浪,會把對象轉(zhuǎn)移到舊生代,然后清空繼續(xù)裝載笛丙,當老年代也滿了后漾脂,就會報outofmemory的異常,