内存实例分析
一、Java內(nèi)存的構(gòu)成
先上一個(gè)官方j(luò)ava document里的圖:? ? 由上圖可知,整塊區(qū)域分為Young Generation、Tenured Generation、Permanent Generation。
詳細(xì)解釋一下Young區(qū): Young區(qū)又分為:Eden、Survivor Space。 Survivor Space又分為 To Survivor、 From Survivor,如下圖所示:
Java內(nèi)存分為 堆內(nèi)存(heap)和 Permanent區(qū)。 1、Java堆內(nèi)存(heap): --是 JVM 用于分配 Java 對(duì)象的內(nèi)存,包含活動(dòng)對(duì)象和不可用對(duì)象? --堆大小通常是在服務(wù)器啟動(dòng)時(shí)使用 java 命令中的 –Xms(最小) –Xmx(最大)標(biāo)志來(lái)定義。? 2、Permanent區(qū): --指內(nèi)存的永久保存區(qū)域 --是Sun JDK和HP JDK用來(lái)加載類(lèi)(class)和Meta信息的專(zhuān)門(mén)的內(nèi)存區(qū) --這個(gè)區(qū)域不歸屬Java堆內(nèi)存(heap)范圍 --Class在被Loader時(shí)就會(huì)被放到此,如果Java應(yīng)用很大,例如類(lèi)(class)很多,那么建議增大這個(gè)區(qū)域的大小來(lái)滿足加載這些類(lèi)的內(nèi)存需求 --通過(guò)–XX:PermSize=***M –XX:MaxPermSize=***M調(diào)整 這里還有一個(gè)本地內(nèi)存的概念: ·本地內(nèi)存(native memory):? --是 JVM 用于其內(nèi)部操作的本地內(nèi)存(非Java內(nèi)存)? --JNI 代碼和第三方本地模塊(例如,本地 JDBC 驅(qū)動(dòng)程序)也使用本地內(nèi)存? --最大本地內(nèi)存大小取決于以下因素:操作系統(tǒng)進(jìn)程內(nèi)存大小限制、已經(jīng)指定用于 Java 堆的內(nèi)存 也就是說(shuō),整個(gè)物理機(jī)的內(nèi)存可以說(shuō)由以下部分構(gòu)成: 物理內(nèi)存 = Java 內(nèi)存 + 本地內(nèi)存 + 操作系統(tǒng)保留的內(nèi)存 二、垃圾回收(Garbage Collection,GC) 1、為什么要垃圾回收 --JVM自動(dòng)檢測(cè)和釋放不再使用的內(nèi)存。? --Java 運(yùn)行時(shí)JVM會(huì)執(zhí)行 GC,這樣程序員不再需要顯式釋放對(duì)象。? 2、垃圾回收(GC)的分類(lèi) --Minor GC --Full GC 3、垃圾回收(GC)的產(chǎn)生過(guò)程 1)新生成的對(duì)象在Eden區(qū)完成內(nèi)存分配 2)當(dāng)Eden區(qū)滿了,再創(chuàng)建對(duì)象,會(huì)因?yàn)樯暾?qǐng)不到空間,觸發(fā)minorGC,進(jìn)行young(eden+1survivor)區(qū)的垃圾回收。(為什么是eden+1survivor:兩個(gè)survivor中始終有一個(gè)survivor是空的,空的那個(gè)被標(biāo)記成To Survivor) 3)minorGC時(shí),Eden不能被回收的對(duì)象被放入到空的survivor(也就是放到To Survivor,同時(shí)Eden肯定會(huì)被清空),另一個(gè)survivor(From Survivor)里不能被GC回收的對(duì)象也會(huì)被放入這個(gè)survivor(To Survivor),始終保證一個(gè)survivor是空的。(MinorGC完成之后,To Survivor 和 From Survivor的標(biāo)記互換) 4)當(dāng)做第3步的時(shí)候,如果發(fā)現(xiàn)存放對(duì)象的那個(gè)survivor滿了,則這些對(duì)象被copy到old區(qū),或者survivor區(qū)沒(méi)有滿,但是有些對(duì)象已經(jīng)足夠Old(通過(guò)XX:MaxTenuringThreshold參數(shù)來(lái)設(shè)置),也被放入Old區(qū) 5)當(dāng)Old區(qū)被放滿的之后,進(jìn)行完整的垃圾回收,即 Full GC 6)Full GC時(shí),整理的是Old Generation里的對(duì)象,把存活的對(duì)象放入到Permanent Generation里。 4、垃圾回收的回收器 --串行(–XX:+UseSerialGC ) Out of Box算法,年輕代串行復(fù)制,年老代串行標(biāo)記整理,主要用于桌面應(yīng)用 --并行(–XX:+UseParallelGC ) 年輕代暫停應(yīng)用程序,多個(gè)垃圾收集線程并行的復(fù)制收集,年老代暫停應(yīng)用程序,與串行收集器一樣,單垃圾收集線程標(biāo)記整理。JDK 6.0啟用該算法后,默認(rèn)啟用了-XX:+UseParallelOldGC,性能大為提高 --并發(fā)(Concurrent Low Pause Collector)( –XX:+UseConcMarkSweepGC ) 啟用該參數(shù),默認(rèn)啟用了-XX:+UseParNewGC;簡(jiǎn)單的說(shuō),并發(fā)是指用戶線程與垃圾收集線程并發(fā),程序在繼續(xù)運(yùn)行,而垃圾收集程序運(yùn)行于其他CPU上。 三、Java內(nèi)存的調(diào)優(yōu)參數(shù) -Xmx1024m: 設(shè)置JVM最大可用內(nèi)存為1024M。 -Xms1024m: 設(shè)置JVM促使內(nèi)存為1024M。此值可以設(shè)置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內(nèi)存。 -Xmn512m: 設(shè)置年輕代大小為512M。(持久代一般固定大小為64m,所以增大年輕代后,將會(huì)減小年老代大小。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。) -Xss128k: 設(shè)置每個(gè)線程的堆棧大小。這個(gè)值可以根據(jù)應(yīng)用的線程所需內(nèi)存大小進(jìn)行調(diào)整。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無(wú)限生成。 -XX:NewRatio=4 設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(總的大小是Xms的值)。設(shè)置為4,則年輕代與年老代所占比值為1:4,年輕代占整個(gè)堆棧的1/5。 舉個(gè)例子,-Xms 設(shè)置為 1024m,-Xmx 也設(shè)置為 1024m的情況下: ·年輕代 = 1024M/5 = 204.8M ·年老代 = 1024M/5*4 = 819.2M 如果-Xms和-Xmx的值設(shè)置的不一樣,可以添加 -XX:MinHeapFreeRatio=<minimum> 和 -XX:MaxHeapFreeRatio=<maximum> 參數(shù),使內(nèi)存的大小能夠在 大于 -Xms 和 小于 -Xmx 之間的范圍內(nèi)自動(dòng)調(diào)整,所以內(nèi)存中會(huì)有Virtual的空間(我是這樣理解的,不是太清楚,這里需要大家指教) 英文原文如下:http://java.sun.com/docs/hotspot/gc1.4.2/#3. Sizing the Generations|outline By default, the virtual machine grows or shrinks the heap at each collection to try to keep the proportion of free space to live objects at each collection within a specific range. This target range is set as a percentage by the parameters -XX:MinHeapFreeRatio=<minimum> and -XX:MaxHeapFreeRatio=<maximum>, and the total size is bounded below by -Xms and above by -Xmx . -XX:SurvivorRatio=4: 設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設(shè)置為4,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:4,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/6 -XX:MaxPermSize=16m: 設(shè)置持久代大小為16m。 -XX:MaxTenuringThreshold=0: 設(shè)置垃圾最大年齡。如果設(shè)置為0的話,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū),直接進(jìn)入年老代。對(duì)于年老代比較多的應(yīng)用,可以提高效率。如果將此值設(shè)置為 一個(gè)較大值,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對(duì)象再年輕代的存活時(shí)間,增加在年輕代即被回收的概論。 總結(jié)如下圖: 四、內(nèi)存分配中會(huì)出現(xiàn)的錯(cuò)誤 關(guān)于內(nèi)存最常見(jiàn)的錯(cuò)誤應(yīng)該是這兩個(gè): ? -- 內(nèi)存溢出 Out Of Memory(OOM) ? -- 內(nèi)存泄露 Memory Leak (ML) 1、內(nèi)存溢出 ? ? 內(nèi)存溢出發(fā)生在這種狀況下:Java內(nèi)存完成Minor GC 之后想要把還存活的對(duì)象放到 Old區(qū) 里,但是這時(shí)Old區(qū) 已經(jīng)滿了,同時(shí) Permanent區(qū)也已經(jīng)放不下存活的對(duì)象。這時(shí)就會(huì)產(chǎn)生 OOM 錯(cuò)誤。 ? 2、內(nèi)存泄露 ? ??在Java中,內(nèi)存泄漏就是存在一些被分配的對(duì)象,這些對(duì)象有下面兩個(gè)特點(diǎn),首先,這些對(duì)象是有被引用的,即在有向樹(shù)形圖中,存 在樹(shù)枝通路可以與其相連;其次,這些對(duì)象是無(wú)用的,即程序以后不會(huì)再使用這些對(duì)象。如果對(duì)象滿足這兩個(gè)條件,這些對(duì)象就可以判定為Java中的內(nèi)存泄漏, 這些對(duì)象不會(huì)被GC所回收,然而它卻占用內(nèi)存。 ? ? 找到一個(gè)例子: ? ? “這 里引用一個(gè)常看到的例子,在下面的代碼中,循環(huán)申請(qǐng)Object對(duì)象,并將所申請(qǐng)的對(duì)象放入一個(gè)Vector中,如果僅僅釋放對(duì)象本身,但因?yàn)?Vector仍然引用該對(duì)象,所以這個(gè)對(duì)象對(duì)GC來(lái)說(shuō)是不可回收的。因此,如果對(duì)象加入到Vector后,還必須從Vector中刪除,最簡(jiǎn)單的方法就是 將Vector對(duì)象設(shè)置為null。
實(shí)際上這些對(duì)象已經(jīng)是無(wú)用的,但還被引用,GC就無(wú)能為力了(事實(shí)上GC認(rèn)為它還有用),這一點(diǎn)是導(dǎo)致內(nèi)存泄漏最重要的原因。”
3、補(bǔ)充一個(gè):PermGen space Error 因?yàn)?GC 不會(huì)在主程序運(yùn)行期對(duì)PermGen space進(jìn)行清理,所以如果應(yīng)用中有很CLASS需要Load的話,就很可能出現(xiàn)PermGen space錯(cuò)誤。 另外如果WEB APP下使用了大量的第三方j(luò)ar, 其大小超過(guò)了 jvm 默認(rèn)的大小那么也會(huì)產(chǎn)生此錯(cuò)誤信息了。 五、總結(jié) 上面4點(diǎn)的內(nèi)容可以跟下面這個(gè)圖來(lái)進(jìn)行融合:與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖
總結(jié)
- 上一篇: WebPack在多页应用项目中的探索
- 下一篇: hdu hide handkerchie