最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java內存溢出(OOM)排查優(yōu)化指南

 更新時間:2025年05月09日 10:32:28   作者:碼到π退休  
OutOfMemoryError,也就是臭名昭著的 OOM(內存溢出),相信很多球友都遇到過,相對于常見的業(yè)務異常,如數(shù)組越界、空指針等,OOM 問題 更難難定位和解決,本文就給大家介紹了Java內存溢出(OOM)排查優(yōu)化指南,需要的朋友可以參考下

前言

OutOfMemoryError,也就是臭名昭著的 OOM(內存溢出),相信很多球友都遇到過,相對于常見的業(yè)務異常,如數(shù)組越界、空指針等,OOM 問題 更難難定位和解決。

這篇內容就以之前碰到的一次線上內存溢出的定位、解決問題的方式展開;希望能對碰到類似問題的球友帶來思路和幫助。

主要從表現(xiàn)-->排查-->定位-->解決 四個步驟來分析和解決問題。

內存溢出和內存泄露

在 Java 中,和內存相關的問題主要有兩種,內存溢出內存泄漏。

  • 內存溢出Out Of Memory):就是申請內存時,JVM 沒有足夠的內存空間。通俗說法就是去蹲坑發(fā)現(xiàn)坑位滿了。
  • 內存泄露Memory Leak):就是申請了內存,但是沒有釋放,導致內存空間浪費。通俗說法就是有人占著茅坑不拉屎。

內存溢出

在 JVM 的內存區(qū)域中,除了程序計數(shù)器,其他的內存區(qū)域都有可能發(fā)生內存溢出。

大家都知道,Java 堆中存儲的都是對象,或者叫對象實例,那只要我們不斷地創(chuàng)建對象,并且保證 GC Roots 到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么就一定會產生內存溢出。

比如說運行下面這段代碼:

public class OOM {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        while (true) {
            list.add(new Object());
        }
    }
}

運行程序的時候記得設置一下 VM 參數(shù):-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError,限制堆內存大小為 20M,并且不允許擴展,并且當發(fā)生 OOM 時 dump 出當前內存的快照。

運行結果如下:

內存泄露

內存泄露是指程序中己動態(tài)分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內存的浪費,導致程序運行速度減慢甚至系統(tǒng)崩潰等嚴重后果。

簡單來說,就是應該被垃圾回收的對象沒有回收掉,導致占用的內存越來越多,最終導致內存溢出。

在上圖中:對象 X 引用對象 Y,X 的生命周期比 Y 的生命周期長,Y 生命周期結束的時候,垃圾回收器不會回收對象 Y。

來看下面的例子:

public class MemoryLeak {
    public static void main(String[] args) {
      try{
          Connection conn =null;
          Class.forName("com.mysql.jdbc.Driver");
          conn =DriverManager.getConnection("url","","");
          Statement stmt =conn.createStatement();
          ResultSet rs =stmt.executeQuery("....");
      } catch(Exception e){//異常日志
      } finally {
        // 1.關閉結果集 Statement
        // 2.關閉聲明的對象 ResultSet
        // 3.關閉連接 Connection
    }
  }
}

創(chuàng)建的連接不再使用時,需要調用 close 方法關閉連接,只有連接被關閉后,GC 才會回收對應的對象(Connection,Statement,ResultSet,Session)。忘記關閉這些資源會導致持續(xù)占有內存,無法被 GC 回收。

這樣就會導致內存泄露,最終導致內存溢出。

換句話說,內存泄露不是內存溢出,但會加快內存溢出的發(fā)生。

內存溢出后的表象

之前生產環(huán)境爆出的內存溢出問題會隨著業(yè)務量的增長,出現(xiàn)的頻次也越來越高。

應用程序的業(yè)務邏輯非常簡單,就是從 Kafka 中將數(shù)據消費下來,然后批量的做持久化操作。

OOM 現(xiàn)象則是隨著 Kafka 的消息越多,出現(xiàn)異常的頻次就越快。由于當時還有其他工作所以只能讓運維做重啟,并且監(jiān)控好堆內存以及 GC 情況。

不得不說,重啟大法真的好,能解決大量的問題,但不是長久之計。

內存泄露的排查

于是我們想根據運維之前收集到的內存數(shù)據、GC 日志嘗試判斷哪里出現(xiàn)了問題。

在這里插入圖片描述

結果發(fā)現(xiàn)老年代的內存使用就算是發(fā)生 GC 也一直居高不下,而且隨著時間推移也越來越高。

結合 jstat 的日志發(fā)現(xiàn)就算是發(fā)生了 FGC,老年代也回收不了,內存已經到頂。

甚至有幾臺應用 FGC 達到了上百次,時間也高的可怕。

這說明應用的內存使用肯定是有問題的,有許多賴皮對象始終回收不掉。

內存泄露的定位

由于生產上的內存 dump 文件非常大,達到了幾十 G。也和我們生產環(huán)境配置的內存太大有關。

所以導致想使用 MAT 分析需要花費大量時間。

MAT 是 Eclipse 的一個插件,也可以單獨使用,可以用來分析 Java 的堆內存,找出內存泄露的原因。

因此我們就想是否可以在本地復現(xiàn),這樣就好定位的多。

為了盡快的復現(xiàn)問題,我將本地應用最大堆內存設置為 150M。然后在消費 Kafka 那里 Mock 了一個 while 循環(huán)一直不斷的生成數(shù)據。

同時當應用啟動之后利用 VisualVM 連上應用實時監(jiān)控內存、GC 的使用情況。

結果跑了 10 幾分鐘內存使用并沒有什么問題。根據圖中可以看出,每一次 GC 內存都能有效的回收,所以并沒有復現(xiàn)問題。

沒法復現(xiàn)問題就很難定位。于是我們就采用了一種古老的方法——review 代碼,發(fā)現(xiàn)生產的邏輯和我們用 while 循環(huán) Mock 的數(shù)據還不太一樣。

果然 review 代碼是保障程序性能的第一道防線,誠不欺我。大家在寫完代碼的時候,盡量也要團隊 review 一次。

后來查看生產日志發(fā)現(xiàn)每次從 Kafka 中取出的都是幾百條數(shù)據,而我們 Mock 時每次只能產生一條。

為了盡可能的模擬生產情況便在服務器上跑了一個生產者程序,一直源源不斷的向 Kafka 中發(fā)送數(shù)據。

果然不出意外只跑了一分多鐘內存就頂不住了,觀察下圖發(fā)現(xiàn) GC 的頻次非常高,但是內存的回收卻是相形見拙。

同時后臺也開始打印內存溢出了,這樣便復現(xiàn)出了問題。

內存泄露的解決

從目前的表現(xiàn)來看,就是內存中有許多對象一直存在強引用關系導致得不到回收。

于是便想看看到底是什么對象占用了這么多的內存,利用 VisualVM 的 HeapDump 功能,就可以立即 dump 出當前應用的內存情況。

結果發(fā)現(xiàn) com.lmax.disruptor.RingBuffer 類型的對象占用了將近 50% 的內存。

看到這個包自然就想到了 Disruptor 環(huán)形隊列了。

Disruptor 是一個高性能的異步處理框架,它的核心思想是:通過無鎖的方式來實現(xiàn)高性能的并發(fā)處理,其性能是高于 JDK 的 BlockingQueue 的。

再次 review 代碼發(fā)現(xiàn):從 Kafka 里取出的 700 條數(shù)據是直接往 Disruptor 里丟的。

這里也就能說明為什么第一次模擬數(shù)據沒復現(xiàn)問題了。

模擬的時候是一個對象放進隊列里,而生產的情況是 700 條數(shù)據放進隊列里。這個數(shù)據量就是 700 倍的差距啊。

而 Disruptor 作為一個環(huán)形隊列,在對象沒有被覆蓋之前是一直存在的。

我也做了一個實驗,證明確實如此。

我設置隊列大小為 8 ,從 0~9 往里面寫 10 條數(shù)據,當寫到 8 的時候就會把之前 0 的位置覆蓋掉,后面的以此類推(類似于 HashMap 的取模定位)。

所以在生產環(huán)境上,假設我們的隊列大小是 1024,那么隨著系統(tǒng)的運行最終會導致 1024 個位置上裝滿了對象,而且每個位置都是 700 個!

于是查看了生產環(huán)境上 Disruptor 的 RingBuffer 配置,結果是:1024*1024。

這個數(shù)量級就非常嚇人了。

為了驗證是否是這個問題,我在本地將該值設為 2 ,一個最小值試試。

同樣的 128M 內存,也是通過 Kafka 一直源源不斷的取出數(shù)據。通過監(jiān)控如下:

跑了 20 幾分鐘系統(tǒng)一切正常,每當一次 GC 都能回收大部分內存,最終呈現(xiàn)鋸齒狀。

這樣問題就找到了,不過生產上這個值具體設置多少還得根據業(yè)務情況測試才能知道,但原有的 1024*1024 是絕對不能再使用了。

小結

雖然到了最后也就改了一行代碼(還沒改,直接修改配置),但這個排查過程我覺得是很有意義的。

也會讓大部分覺得 JVM 這樣的黑盒難以下手的球友有一個直觀感受。

同時也得感嘆 Disruptor 東西雖好,也不能亂用哦!

以上就是Java內存溢出(OOM)排查優(yōu)化指南的詳細內容,更多關于Java內存溢出OOM的資料請關注腳本之家其它相關文章!

相關文章

  • Spring?中?PageHelper?不生效問題及解決方法

    Spring?中?PageHelper?不生效問題及解決方法

    這篇文章主要介紹了Spring?中?PageHelper?不生效問題,使用這個插件時要注意版本的問題,不同的版本可能 PageHelper 不會生效,本文結合示例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-12-12
  • SpringBoot中的ImportSelector類動態(tài)加載bean詳解

    SpringBoot中的ImportSelector類動態(tài)加載bean詳解

    這篇文章主要介紹了SpringBoot中的ImportSelector類動態(tài)加載bean詳解,ImportSelector接口是spring中導入外部配置的核心接口,根據給定的條件(通常是一個或多個注釋屬性)判定要導入那個配置類,在spring自動化配置和@EnableXXX中都有它的存在,需要的朋友可以參考下
    2024-01-01
  • spring中@Bean和@Component的區(qū)別及說明

    spring中@Bean和@Component的區(qū)別及說明

    文章主要介紹了@Bean和@Component兩個注解在Spring框架中的定義、作用范圍、創(chuàng)建方式、掃描和識別機制以及使用場景和建議
    2024-12-12
  • SpringBoot集成ElaticJob定時器的實現(xiàn)代碼

    SpringBoot集成ElaticJob定時器的實現(xiàn)代碼

    這篇文章主要介紹了SpringBoot集成ElaticJob定時器的實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • SpringCloud中的Hystrix保護機制詳解

    SpringCloud中的Hystrix保護機制詳解

    這篇文章主要介紹了SpringCloud中的Hystrix保護機制詳解,Hystrix,英文意思是豪豬,全身是刺,看起來就不好惹,是一種保護機制,Hystrix也是Netflix公司的一款組件,需要的朋友可以參考下
    2023-12-12
  • 深入理解Mybatis一級緩存

    深入理解Mybatis一級緩存

    客戶端向數(shù)據庫服務器發(fā)送同樣的sql查詢語句,如果每次都去訪問數(shù)據庫,會導致性能的降低,那么怎么提高呢?下面小編給大家分享下mybatis為我們提供了一級緩存的策略
    2016-12-12
  • Java總結篇系列:Java泛型詳解

    Java總結篇系列:Java泛型詳解

    下面小編就為大家?guī)硪黄狫ava總結篇系列:Java泛型詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • SpringBoot Controller Post接口單元測試示例

    SpringBoot Controller Post接口單元測試示例

    今天小編就為大家分享一篇關于SpringBoot Controller Post接口單元測試示例,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • SpringMVC中文件的上傳與下載詳細解析

    SpringMVC中文件的上傳與下載詳細解析

    這篇文章主要介紹了SpringMVC中文件的上傳與下載詳細解析,在開發(fā)中有遇到文件上傳下載的功能需求,今天就來說一下前后端的實現(xiàn)和要注意的地方,需要的朋友可以參考下
    2024-01-01
  • Spring Boot中擴展XML請求與響應的支持詳解

    Spring Boot中擴展XML請求與響應的支持詳解

    這篇文章主要給大家介紹了關于Spring Boot中擴展XML請求與響應的支持的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09

最新評論

黄浦区| 连南| 石楼县| 黑河市| 噶尔县| 桐梓县| 合水县| 屯门区| 百色市| 洮南市| 厦门市| 津市市| 司法| 新巴尔虎左旗| 陕西省| 巴林左旗| 永平县| 阿拉善右旗| 滦平县| 东平县| 鄂托克旗| 林西县| 恩平市| 若羌县| 堆龙德庆县| 恩平市| 东平县| 鹤岗市| 兰坪| 顺义区| 永登县| 东方市| 长丰县| 息烽县| 南木林县| 民丰县| 延边| 孟村| 湟源县| 尼勒克县| 哈巴河县|