內存溢出與內存泄漏
- 內存溢出相對於內存泄漏來說,盡管更容易被理解,但是同樣的,內存溢出也是引發程序崩潰的罪魁禍首之一。
- 由於GC一直在發展,所有一般情況下,除非應用程序占用的內存增長速度非常快,造成垃圾回收已經跟不上內存消耗的速度,否則不太容易出現OOM的情況。
- 大多數情況下,GC會進行各種年齡段的垃圾回收,實在不行了就放大招,來一次獨占式的Full GC操作,這時候會回收大量的內存,供應用程序繼續使用。
- javadoc中對OutOfMemoryError的解釋是,沒有空閑內存,並且垃圾收集器也無法提供更多內存(意思是即使GC之后,也無法提供空閑的內存)。
內存溢出
- 首先說沒有空閑內存的情況:說明Java虛擬機的堆內存不夠。原因有二:
- (1) Java虛擬機的堆內存設置不夠。
比如:可能存在內存泄漏問題;也很有可能就是堆的大小不合理,比如我們要處理比較可觀的數據量,但是沒有顯式指定JVM堆大小或者指定數值偏小。我們可以通過參數一Xms、一Xmx來調整。 - (2)代碼中創建了大量大對象,並且長時間不能被垃圾收集器收集(存在被引用)對於老版本的Oracle JDK,因為永久代的大小是有限的,並且JVM對永久代垃圾回收(如,常量池回收、卸載不再需要的類型)非常不積極,所以當我們不斷添加新類型的時候,永久代出現OutOfMemoryError也非常多見,尤其是在運行時存在大量動態類型生成的場合;類似intern字符串緩存占用太多空間,也會導致0OM問題。對應的異常信息,會標記出來和永久代相關: "java. lang. OutOfMemoryError: PermGen space"。
隨着元數據區的引入,方法區內存已經不再那么窘迫,所以相應的00M有所改觀,出現00M,異常信息則變成了:“java. lang. OutOfMemoryError: Metaspace"。 直接內存不足,也會導致0OM。
- (1) Java虛擬機的堆內存設置不夠。
- 這里面隱含着一層意思是,在拋出0utOfMemoryError之 前,通常垃圾收集器會被觸發,盡其所能去清理出空間。
- ➢例如:在引用機制分析中,涉及到JVM會去嘗試回收軟引用指向的對象等。
- ➢在java.nio.BIts.reserveMemory()方法中,我們能清楚的看到,System.gc()會被調用,以清理空間。
- 當然,也不是在任何情況下垃圾收集器都會被觸發的
- ➢比如,我們去分配一個超大對象,類似一個超大數組超過堆的最大值,JVM可以判斷出垃圾收集並不能解決這個問題,所以直接拋出OutOfMemoryError
內存泄漏(Memory Leak)
- 也稱作“存儲滲漏”。嚴格來說,只有對象不會再被程序用到了,但是GC又不能回收他們的情況,才叫內存泄漏。
- 但實際情況很多時候一些不太好的實踐(或疏忽)會導致對象的生命周期變得很長甚至導致OOM,也可以叫做寬泛意義上的“內存泄漏。
- 盡管內存泄漏並不會立刻引起程序崩潰,但是一旦發生內存泄漏,程序中的可用內存就會被逐步蠶食,直至耗盡所有內存,最終出現OutOfMemory異常,導致程序崩潰。
注意,這里的存儲空間並不是指物理內存,而是指Java層面JVM虛擬內存大小,這個虛擬內存大小取決於磁盤交換區設定的大小。

Forgotten Reference:由於某種原因沒有/忘記斷開的引用,導致內存泄漏。