JVM學習筆記(3)---OutOfMemory詳解
堆溢位
之前說過,堆中主要儲存的是物件例項。
所以如果不斷建立物件,並保證GC Roots(之後會說明)到物件間有可達路徑來避免垃圾回收機制清除這些物件,就會在物件數量達到堆的容量限制後產生記憶體溢位。
異常示例程式碼如下:
/** * 堆溢位 * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * -Xms設定堆的最小值 -Xmx設定堆的最大值 * */public class HeapOOM { static class OOMObject { } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while (true) { list.add(new OOMObject()); } } }
透過-Xms和-Xmx設定Vm最小、最大堆的值為20m(將最大最小值設為一樣可以避免堆的自動擴充套件)
棧溢位
HotSpot虛擬機器不區分虛擬機器棧和本地方法棧,所以這裡統稱為棧溢位。
之前已經討論過,虛擬機器棧會丟擲兩種異常,StackOverflowError 和 OutOfMemoryError 異常。(見
Java虛擬機器規範中,對該區域規定了兩種異常:
StackOverFlowError:執行緒請求的棧深度大於虛擬機器允許的棧深度時
OverOfMemoryError:動態擴充套件的執行緒無法申請到足夠的記憶體時
簡單理解來看:
對於前者而言,是由於增加過大棧幀深度或限制虛擬機器棧記憶體產生。
在單執行緒中,我們不斷增大棧幀中本地變數表的長度(如定義大量的本地變數),或者限制棧記憶體容量(透過Vm啟動引數),都輸出StackOverFlowError。對於後者而言,是由於不斷建立執行緒產生。
這樣產生的 OutOfMemoryError 與棧空間大小不存在關係。在記憶體總量一定時,每個執行緒的棧分配的記憶體越大,則越容易產生記憶體溢位。
異常示例程式碼如下:
/** * 棧溢位 OutOfMemoryError * VM Args:-Xss2m * -Xss設定棧大小 來減小棧容量 * 執行本程式碼前請先儲存當前電腦環境,可能假死 */public class StackOOM { public int threadCount = 0; public void addThread() { while (true) { Thread thread = new Thread(new Runnable() { public void run() { while (true) { } } }); thread.start(); } } public static void main(String[] args){ StackOOM stackOOM = new StackOOM(); stackOOM.addThread(); } }
直接記憶體溢位
在JDK1.4之後,新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可以直接使用Native函式庫分配堆外記憶體,透過一個儲存在Java堆中的 DirectByteBuffer物件 作為這塊記憶體的引用進行操作。
這樣能在一些場景中顯著提高效能,避免了在Java堆和Native堆中來回複製資料。
透過建立Unsafe例項,可以申請分配直接記憶體。透過MaxDirectMemorySize引數,可以指定直接記憶體容量,當申請的直接記憶體過大時,會出現直接記憶體溢位。
異常示例程式碼如下:
/** * Vm Args: -Xmx20M -xx:MaxDirectMemorySize=10M * */public class DirectMemoryOOM { private static final int _1MB = 1024*1024; public static void main(String[] args)throws Exception{ //透過反射獲取Unsafe例項,來申請記憶體分配 Field unsafeFiled = Unsafe.class.getDeclaredFields()[0]; unsafeFiled.setAccessible(true); Unsafe unsafe = (Unsafe)unsafeFiled.get(null); while (true){ unsafe.allocateMemory(_1MB); } } }
元空間溢位
我們已知,方法區中主要存放的是一些描述性資訊,即後設資料。元空間是方法區的一種實現方式。
(注意,方法區是一種規範,元空間 和 永久代 都是一種實現。
在JDK1.8之前,使用 永久代(PermGen)來實現方法區,但有以下問題:
永久代記憶體經常不夠用或發生記憶體洩露,爆出異常 OutOfMemoryError: PermGen
動態類載入的情況越來越多,這塊記憶體我們變得不太可控。
類及方法的資訊等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢位,太大則容易導致老年代溢位。
永久代會為 GC 帶來不必要的複雜度,並且回收效率偏低。
在jdk8之後,用元空間(MetaSpace)替代。 元空間是和本地記憶體相關的。預設上限大小是本地記憶體。
元空間與永久代之間 最大區別 在於:
元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制。
異常程式碼如下:
import net.sf.cglib.beans.BeanGenerator;import net.sf.cglib.beans.BeanMap;import java.util.*;/** * VM args: -XX:MaxMetaspaceSize=1M -XX:+PrintGCDetails */public class MetaSpaceOOM { public static void main(String[] args) throws Exception { for (int i = 0; i < 80000; i++) { //動態建立類 Map<Object, Object> propertyMap = new HashMap<Object, Object>(); propertyMap.put("id", Class.forName("java.lang.Integer")); //建立一個動態生成bean CglibBean bean = new CglibBean(propertyMap); //給 Bean 設定值 bean.setValue("id", new Random().nextInt(100)); //列印 Bean的屬性id System.out.println("num=" + i + " id=" + bean.getValue("id")); } } static class CglibBean { /** * 實體Object */ public Object object = null; /** * 屬性map */ public BeanMap beanMap = null; public CglibBean() { super(); } @SuppressWarnings("unchecked") public CglibBean(Map propertyMap) { this.object = generateBean(propertyMap); this.beanMap = BeanMap.create(this.object); } /** * 給bean屬性賦值 * @param property 屬性名 * @param value 值 */ public void setValue(String property, Object value) { beanMap.put(property, value); } /** * 透過屬性名得到屬性值 * @param property 屬性名 * @return 值 */ public Object getValue(String property) { return beanMap.get(property); } /** * 得到該實體bean物件 * @return */ public Object getObject() { return this.object; } @SuppressWarnings("unchecked") private Object generateBean(Map propertyMap) { BeanGenerator generator = new BeanGenerator(); Set keySet = propertyMap.keySet(); for (Iterator i = keySet.iterator(); i.hasNext();) { String key = (String) i.next(); generator.addProperty(key, (Class) propertyMap.get(key)); } return generator.create(); } } }
作者:weberweber
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3209/viewspace-2818118/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- JVM學習筆記JVM筆記
- JVM學習筆記——初識JVMJVM筆記
- JVM核心學習筆記JVM筆記
- JVM 學習筆記(五)JVM筆記
- 今日學習JVM筆記JVM筆記
- JVM學習筆記-01JVM筆記
- Go學習筆記-GMP詳解Go筆記
- JVM學習筆記——Class類檔案解讀JVM筆記
- JVM學習筆記之棧區JVM筆記
- MIT 6.824 學習筆記(一)--- RPC 詳解MIT筆記RPC
- Nginx變數詳解(學習筆記十九)Nginx變數筆記
- mysql學習筆記-底層原理詳解MySql筆記
- JVM學習筆記——自動記憶體管理JVM筆記記憶體
- git checkout 命令詳解—— Git 學習筆記 16Git筆記
- JavaWeb學習筆記_Day03_JavaScript詳解Web筆記JavaScript
- tensorflow學習筆記3筆記
- Vue學習筆記3Vue筆記
- mysql學習筆記3MySql筆記
- Vue 3 學習筆記Vue筆記
- JVM學習筆記---伺服器,JVM效能監控工具JVM筆記伺服器
- JVM學習筆記——節碼執行引擎JVM筆記
- java學習筆記-4 JVM垃圾回收(GC)Java筆記JVMGC
- jvm學習筆記6:類載入器JVM筆記
- JVM學習筆記——類載入機制JVM筆記
- JVM學習筆記(4)---垃圾收集器JVM筆記
- JVM狂神說視訊學習筆記JVM筆記
- git cherry-pick 詳解 —— Git 學習筆記 18Git筆記
- git reset 命令詳解(一)—— Git 學習筆記 07Git筆記
- git reset 命令詳解(二)—— Git 學習筆記 08Git筆記
- 強化學習-學習筆記3 | 策略學習強化學習筆記
- swift學習筆記《3》-技巧Swift筆記
- Vue3 學習筆記Vue筆記
- docker學習筆記(3)- 映象Docker筆記
- JVM學習筆記五--虛擬機器棧JVM筆記虛擬機
- kernel_mktime() 詳解 —— Linux-0.11 學習筆記(四)Linux筆記
- JVM記憶體分配機制與回收策略選擇-JVM學習筆記(2)JVM記憶體筆記
- 3月25日學習筆記筆記
- Spring Boot 學習筆記(3):MyBatisSpring Boot筆記MyBatis