Java堆java
java堆用於存儲對象實例,只要不斷的建立對象,而且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那麼當對象數量達到最大堆容量限制後就會產生OutOfMemoryError異常。當出現java堆內存溢出時,異常堆棧信息「java.lang.OutOfMemoryError」會跟着進一步提示「Java heap space,以下:多線程
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at com.test.outofmemory.HeapOOM.main(HeapOOM.java:25)框架
解決方式:jvm
設置Eclipse的jvm參數:測試
-Xms256M -Xmx512M -XX:PermSize=256m -XX:MaxPermSize=512mspa
2.Java虛擬機棧和本地方法棧溢出操作系統
關於java虛擬機棧和本地方法棧,在java虛擬機中描述了兩種異常:線程
若是線程請求的棧深度大於虛擬機容許的最大深度,將拋出StackOverflowError異常代理
若是虛擬機在擴展棧時沒法申請到足夠的內存空間,則拋出OutOfMemoryError異常(OutOfMemoryError unable to cteate new native thread)。code
解決方式:
-Xss:設置每條線程的Statck大小.在JDK1.5之後默認是1M,以前是256K
拋出StackOverFlow異常:操做系統分配給每一個線程的內存是有限的,機器總內存減去Xmx再減去MaxPermSize,程序計數器佔內存不多忽略,剩下的內存被虛擬機棧和本地方法棧瓜分,每一個線程分到的棧容量越大,分配的線程數就小。正常狀況棧深度1000-2000沒問題,若是是創建更多線程致使的內存溢出,在不能減小線程的狀況下,只能經過減少Xmx和棧容量來換取更多線程。
3.方法區和運行時常量池溢出
若是要向運行時常量池中添加內容,最簡單的作法就是使用String.intern()這個Native方法。該方法的做用是:若是池中已經包含一 個等於此String對象的字符串,則返回表明池中這個字符串的String對象;不然,將此String對象包含的字符串添加到常量池中,而且返回此 String對象的引用。因爲常量池分配在方法區內,咱們能夠經過-XX:PermSize和-XX:MaxPermSize限制方法區的大小,從而間接 限制其中常量池的容量,如代碼所示,運行時常量池致使的內存溢出異常。
/** * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M * @author zzm */ public class RuntimeConstantPoolOOM { public static void main(String[] args) { // 使用List保持着常量池引用,避免Full GC回收常量池行爲 List<String> list = new ArrayList<String>(); // 10MB的PermSize在integer範圍內足夠產生OOM了 int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } }
運行結果:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.test.outofmemory.HeapOOM.main(HeapOOM.java:34)
從運行結果中能夠看到,運行時常量池溢出,在OutOfMemoryError後面跟隨的提示信息是「PermGen space」,說明運行時常量池屬於方法區(HotSpot虛擬機中的永久代)的一部分。
方法區用於存放Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。對於這些區域的測試,基本的思路是運行時產生大量的類去填滿方法區,直到溢出。
雖然直接使用Java SE API也能夠動態產生類(如反射時的GeneratedConstructorAccessor和動態代理等),但在本次實驗中操做起來比較麻煩。在代碼清單2-8中,筆者藉助CGLib直接操做字節碼運行時生成了大量的動態類。
值得特別注意的是,咱們在這個例子中模擬的場景並不是純粹是一個實驗,這樣的應用常常會出如今實際應用中:當前的不少主流框架,如Spring、 Hibernate,在對類進行加強時,都會使用到CGLib這類字節碼技術,加強的類越多,就須要越大的方法區來保證動態生成的Class能夠加載入內 存。另外,JVM上的動態語言(例如Groovy等)一般都會持續建立類來實現語言的動態性,隨着這類語言的流行,也愈來愈容易遇到與代碼清單2-8類似 的溢出場景。
代碼清單2-8 藉助CGLib使方法區出現內存溢出異常
/** * * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M * */ public class JavaMethodAreaOOM { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); } } static class OOMObject { } }
運行結果:
Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616) ... 8 more
方法區溢出也是一種常見的內存溢出異常,一個類要被垃圾收集器回收掉,斷定條件是比較苛刻的。在常常動態生成大量Class的應用中,須要特別注意類的回 收情況。這類場景除了上面提到的程序使用了CGLib字節碼加強和動態語言以外,常見的還有:大量JSP或動態產生JSP文件的應用(JSP第一次運行時 須要編譯爲Java類)、基於OSGi的應用(即便是同一個類文件,被不一樣的加載器加載也會視爲不一樣的類)等。
解決方式:
-PermSize :方法區的初始容量,默認是物理內存的1/64
-MaxPermSize :最大方法區容量。
4.本地直接內存溢出
並非虛擬機運行時數據區的一部分。JDK1.4中引入了NIO類,引入了一種基於通道與緩衝區的I/O方式,可使用Native直接分配堆外內存,避免了再Java堆和Native堆中來回複製數據。
不會受到Java堆內存限制,但會受到機器總內存的限制。
若是如法申請到足夠的空間拋出OutOfMemoryError。
解決方式:-XX:MaxDirectMemorySize 本機直接內存大小,若是不指定,則與Xmx同樣。