91在线视频播放|成人黄视频在线观看|在线视频福利|天天欲色成人综合网站|国产国语videosex护士

機構檔案
  • 機構級別:普通會員
  • 信用等級:

在線交談:點擊這里給我發消息

咨詢熱線:029-62258374

學校評價(我要提問/點評)

  • 學校被點評:0
  • 好評(0%)
  • 中評(0%)
  • 差評(0%)

資料認證

    未通過身份證認證 未通過身份證認證

    未通過辦學許可認證 未通過辦學許可認證

  • 學校瀏覽人次:
  • 加盟時間:2017年03月10日
新聞動態

西安尚學堂java:Java常見內存溢出異常與代碼實現

發布者:西安尚學堂 發布時間:2017-04-17 來源:西安尚學堂

Java堆是用來存儲對象實例的,因此如果我們不斷地創建對象,并且保證GC Root和創建的對象之間有可達路徑以免對象被垃圾回收,那么當創建的對象過多時,會導致heap內存不足,進而引發OutOfMemoryError異常.本文西安尚學堂java培訓專家為大家整理了Java常見內存溢出異常與代碼實現:

/**

* @author xiongyongshun

* VM Args: java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError

*/

public class OutOfMemoryErrorTest {

public static void main(String[] args) {

List list = new ArrayList<>();

int i = 0;

while (true) {

list.add(i++);

}

}

}

上面是一個引發OutOfMemoryError異常的代碼,我們可以看到,它就是通過不斷地創建對象,并將對象保存在list中防止其被垃圾回收,因此當對象過多時,就會使堆內存溢出.

通過java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError我們設置了堆內存為10兆,并且使用參數-XX:+HeapDumpOnOutOfMemoryError讓JVM在發生OutOfMemoryError異常時打印出當前的內存快照以便于后續分析.

編譯運行上述代碼后,會有如下輸出:

>>> java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError com.test.OutOfMemoryErrorTest16-10-02 23:35

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid1810.hprof ...

Heap dump file created [14212861 bytes in 0.125 secs]

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at java.util.Arrays.copyOf(Arrays.java:3210)

at java.util.Arrays.copyOf(Arrays.java:3181)

at java.util.ArrayList.grow(ArrayList.java:261)

at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)

at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)

at java.util.ArrayList.add(ArrayList.java:458)

at com.test.OutOfMemoryErrorTest.main(OutOfMemoryErrorTest.java:15)

Java棧StackOverflowError

我們知道, JVM的運行時數據區中有一個叫做虛擬機棧的內存區域,此區域的作用是:每個方法在執行時都會創建一個棧幀,用于存儲局部變量表,操作數棧,方法出口等信息.

因此我們可以創建一個無限遞歸的遞歸調用,當遞歸深度過大時,就會耗盡棧空間,進而導致了StackOverflowError異常.

下面是具體的代碼:

/**

* @author xiongyongshun

* VM Args: java -Xss64k

*/

public class OutOfMemoryErrorTest {

public static void main(String[] args) {

stackOutOfMemoryError(1);

}public static void stackOutOfMemoryError(int depth) {

depth++;

stackOutOfMemoryError(depth);

}

}

當編譯運行上述的代碼后,會輸出如下異常信息:

Exception in thread "main" java.lang.StackOverflowError

at com.test.OutOfMemoryErrorTest.stackOutOfMemoryError(OutOfMemoryErrorTest.java:27)

方法區內存溢出

注意,因為JDK8已經移除了永久代,取而代之的是metaspace,因此在JDK8中,下面兩個例子都不會導致java.lang.OutOfMemoryError: PermGen space異常.

運行時常量池溢出

在Java 1.6以及之前的HotSpot JVM版本時,有永久代的概念,即GC的分代收集機制是擴展至方法區的.在方法區中,有一部分內存是用于存儲常量池,因此如果代碼中常量過多時,就會耗盡常量池內存,進而導致內存溢出.

那么如何添加大量的常量到常量池呢?這時就需要依靠String.intern()方法了. String.intern()方法的作用是:若此String的值在常量池中已存在,則這個方法返回常量池中對應字符串的引用;反之將此String所包含的值添加到常量池中,并返回此String對象的引用.在JDK 1.6以及之前的版本中,常量池分配在永久代中,因此我們可以通過設置參數"-XX:PermSize"和"-XX:MaxPermSize"來間接限制常量池的大小.

注意,上面所說的String.intern()方法和常量池的內存分布僅僅針對于JDK 1.6及之前的版本,在JDK 1.7或以上的版本中,由于去除了永久代的概念,因此內存布局稍有不同.

下面是實現常量池內存溢出的代碼例子:

/**

* @author xiongyongshun

* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

*/

public class RuntimeConstantPoolOOMTest {

public static void main(String[] args) {

List list = new ArrayList();

int i = 0;

while (true) {

list.add(String.valueOf(i++).intern());

}

}

}

我們看到,這個例子中,正是使用了String.intern()方法,向常量池中添加了大量的字符串常量,因而導致了常量池的內存溢出.

我們通過JDK1.6編譯并運行上面的代碼,會有如下輸出:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

at java.lang.String.intern(Native Method)

at com.test.RuntimeConstantPoolOOMTest.main(RuntimeConstantPoolOOMTest.java:16)

需要注意的是,如果通過JDK1.8來編譯運行上面代碼的話,會有如下警告,并且不會產生任何的異常:

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0

方法區的內存溢出

方法區作用是存放Class的相關信息,例如類名,類訪問修飾符,字段描述,方法描述等.因此如果方法區過小,而加載的類過多,就會造成方法區的內存溢出.

//VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

public class MethodAreaOOMTest {

public static void main(String[] args) {

while (true) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(MethodAreaOOMTest.class);

enhancer.setUseCache(false);

enhancer.setCallback(new MethodInterceptor() {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

return methodProxy.invokeSuper(o, objects);

}

});enhancer.create();

}

}

}

上面的代碼中,我們借助CGlib來動態地生成大量的類,在JDK6下,運行上面的代碼會產生OutOfMemoryError: PermGen space異常:

/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java -jar -XX:PermSize=10M -XX:MaxPermSize=10M target/Test-1.0-SNAPSHOT.jar

輸出結果如下:

Caused by: java.lang.OutOfMemoryError: PermGen space

at java.lang.ClassLoader.defineClass1(Native Method)

at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637)

at java.lang.ClassLoader.defineClass(ClassLoader.java:621)

... 11 more

MetaSpace內存溢出

在方法區的內存溢出內存溢出一節中,我們提到, JDK8沒有了永久代的概念,因此那兩個例子在JDK8下沒有實現預期的效果.那么在JDK8下,是否有類似方法區內存溢出之類的錯誤呢?當然有的.在JDK8中,使用了MetaSpace的區域來存放Class的相關信息,因此當MetaSpace內存空間不足時,會拋出java.lang.OutOfMemoryError: Metaspace異常.

我們還是以上面提到的例子為例:

//VM Args: -XX:MaxMetaspaceSize=10M

public class MethodAreaOOMTest {

public static void main(String[] args) {

while (true) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(MethodAreaOOMTest.class);

enhancer.setUseCache(false);

enhancer.setCallback(new MethodInterceptor() {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

return methodProxy.invokeSuper(o, objects);

}

});enhancer.create();

}

}

}

此例子的代碼部分沒有改動,唯一的區別是我們需要使用JDK來運行這段代碼,并且設著參數-XX:MaxMetaspaceSize=10M,這個參數告訴JVM Metaspace的最大大小是10M.

接著我們使用JDK8來編譯運行這個例子,輸出如下異常:

>>> java -jar -XX:MaxMetaspaceSize=10M target/Test-1.0-SNAPSHOT.jar

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)

at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)

at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)

at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)

at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)

at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)

at com.test.MethodAreaOOMTest.main(MethodAreaOOMTest.java:22)