個人主頁
文章專欄
在正文開始前,我想多說幾句,也就是吐苦水吧…最近這段時間一直想寫點東西,停下來反思思考一下。
心中萬言,真正執筆時又不知先寫些什么。通常這個時候,我都會隨便寫寫,文風極像散文,形散意不散吧!
先說一下近況,最近參加了Mathorlab數學建模,作為一個大一的學生,第一次參加那么高強度的競賽。深深的意識到自己的不足,天之大,不過蚍蜉撼樹。我不過是渺小的滄海一粟。竟欲與蒼天比高,不自量力、癡人說夢。夢醒了,還是得加油干…高考后,天真的以為自己不用學習了,后來發現,自己一輩子都要學習。害,挺難過的,也挺無助的。
我,出現在這里。可能你們以為我是理科生,工科女…哦不!我江蘇高考純文出身,這像極了案底。高二下之前,我的確是根正苗紅的物化生。可惜,爾輩不能究物理。我無比后悔當時的決定,可是人是不會滿足的,那時候不管學啥,我都會后悔。用現在的眼光,去埋怨當時的自己。這像極了霸凌…
我本科財務管理專業,但是我偏離了軌道。去學了計算機…午夜夢回,總會覺著自己可笑。一個文科生,還不是很好的大學,去學了計算機…很瘋狂!看到這兒,你可能會笑話我…太不自量力了!但是,哎!
其實,當我步入大學沒多久我就意識到這個軌道的盡頭是什么。
如果你問我讀書的意義是什么?上年這個時候,我可能還會說:為天地立心,為生民立命…多么正氣,多么理想化!但是,現在我只想以后我有能力養活自己。
唉,我也不知道我的選擇是不是正確的。我也不知道,我未來的我怎么去看待這個選擇,越想越內耗。
我和我朋友說,我考研要考南京郵電大學…我也不知道未來怎么樣,這是19歲的我選擇的最適合我的出路了。反正認準了就好好去努力吧。就算錯了,從頭再來就是了。我才不過19歲,我最大的優勢就是年輕…
共勉吧!
文章目錄
- 一、Java 堆溢出
- 原理
- 示例代碼
- 解決思路
- 二、虛擬機棧和本地方法棧溢出
- 原理
- 示例代碼
- 解決思路
- 三、方法區和運行時常量池溢出
- 原理
- 示例代碼
- 解決思路
- 四、直接內存溢出
- 原理
- 示例代碼
在 Java 開發中,內存溢出異常是影響程序穩定性的關鍵問題。了解其原理和應對方法,對開發者至關重要。
一、Java 堆溢出
原理
Java 堆用于存儲對象實例。不斷創建對象,且阻止垃圾回收器回收,對象數量超出堆容量時,就會引發堆溢出。
示例代碼
// VM Args: -Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError
public class HeapOOM {static class OOMObject {}public static void main(String[] args) {List<OOMObject> list = new ArrayList<>();while (true) {list.add(new OOMObject());}}
}
解決思路
- 利用內存映像分析工具(如 Eclipse Memory Analyzer )分析堆轉儲快照。
- 區分內存泄漏和內存溢出:若存在無用對象長期占用內存,是內存泄漏;若對象都有用但堆空間不足,可調整堆參數(-Xmx 與 - Xms ),并優化代碼減少內存占用。
二、虛擬機棧和本地方法棧溢出
原理
- 線程請求棧深度超虛擬機允許值,拋出
StackOverflowError
。 - 虛擬機棧若支持動態擴展,擴展時內存申請失敗,拋出
OutOfMemoryError
(HotSpot 不支持棧動態擴展 )。
示例代碼
- 測試
StackOverflowError
// VM Args: -Xss128k
public class JavaVMStackSOF {private int stackLength = 1;public void stackLeak() {stackLength++;stackLeak();}public static void main(String[] args) throws Throwable {JavaVMStackSOF oom = new JavaVMStackSOF();try {oom.stackLeak();} catch (Exception e) {System.out.println("stack length:" + oom.stackLength);throw e;}}
}
- 測試大量線程導致內存溢出
// VM Args: -Xss2M
public class JavaVMStackOOM {private void dontStop() {while (true) {}}public void stackLeakByThread() {while (true) {Thread thread = new Thread(() -> dontStop());thread.start();}}public static void main(String[] args) throws Throwable {JavaVMStackOOM oom = new JavaVMStackOOM();oom.stackLeakByThread();}
}
解決思路
- 出現
StackOverflowError
時,可根據錯誤堆棧分析遞歸調用等問題代碼。 - 對于大量線程導致的內存溢出,可減少線程數量、調整棧內存大小(-Xss ),或升級 64 位虛擬機以獲取更多內存。
三、方法區和運行時常量池溢出
原理
- 方法區存儲類信息、常量池等。運行時動態生成大量類(如使用 CGLib ),會耗盡方法區空間。
- 運行時常量池是方法區一部分,字符串操作(如
String.intern()
)不當,可能導致常量池溢出。
示例代碼
- 方法區溢出測試(借助 CGLib )
// VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class JavaMethodAreaOOM {static class OOMObject {}public static void main(String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, args);}});enhancer.create();}}
}
- 運行時常量池溢出測試(
String.intern()
)
// JDK 6 運行:-XX:PermSize=6M -XX:MaxPermSize=6M
// JDK 7及以上運行:-Xmx6M
public class RuntimeConstantPoolOOM {public static void main(String[] args) {String str1 = new StringBuilder("計算機").append("軟件").toString();System.out.println(str1.intern() == str1); String str2 = new StringBuilder("ja").append("va").toString();System.out.println(str2.intern() == str2); }
}
解決思路
- 方法區溢出時,調整方法區相關參數(如 JDK 8 前的 - XX:PermSize 和 - XX:MaxPermSize ,JDK 8 及以后的 - XX:MetaspaceSize 等 ),優化代碼減少動態類生成。
- 針對常量池溢出,合理使用
String.intern()
方法,避免無意義的字符串入池操作。
四、直接內存溢出
原理
直接內存容量由-XX:MaxDirectMemorySize
參數控制,默認與 Java 堆最大值相同。直接或間接使用DirectByteBuffer
、Unsafe
等分配內存超出限制,會引發溢出。
示例代碼
// VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class DirectMemoryOOM {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws Exception {Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while (true) {unsafe.allocateMemory(_1MB);}}
}
解決思路
- 合理設置
-XX:MaxDirectMemorySize
參數。 - 排查代碼中直接內存分配操作,如 NIO 相關代碼,確保內存分配合理。
ld.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
解決思路- 合理設置`-XX:MaxDirectMemorySize` 參數。
- 排查代碼中直接內存分配操作,如 NIO 相關代碼,確保內存分配合理。通過深入理解 Java 內存溢出異常原理,結合具體代碼示例和解決思路,開發者能更好地定位和解決內存問題,保障 Java 程序穩定運行。