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