主頁 > 移動端開發 > 這是什么重大發現!一個導致JVM物理記憶體消耗大的Bug

這是什么重大發現!一個導致JVM物理記憶體消耗大的Bug

2020-09-17 13:57:52 移動端開發

這是什么重大發現!一個導致JVM物理記憶體消耗大的Bug

概述

最近我們公司在幫一個客戶查一個JVM的問題(JDK1.8.0_191-b12),發現一個系統老是被OS Kill掉,是記憶體泄露導致的,在查的程序中,陰差陽錯地發現了JVM另外的一個Bug,這個Bug可能會導致大量物理記憶體被使用,我們已經反饋給了社區,并得到快速反饋,預計在OpenJDK8最新版中發布(JDK11中也存在這個問題),

這是什么重大發現!一個導致JVM物理記憶體消耗大的Bug

PS:用戶的那個問題最終也解決了,定位下來算是C2的一個設計缺陷導致大量記憶體被使用,安全性上沒有得到保障,

找出消耗大記憶體的執行緒

接下來主要分享下這個BUG的發現程序,先要客戶實時跟蹤行程的情況,當記憶體使用明顯上升的時候,通過/proc/<pid>/smaps,看到了不少64MB的記憶體分配,Rss也基本消耗完了,

7fd690000000-7fd693f23000 rw-p 00000000 00:00 0 
Size:              64652 kB
Rss:               64652 kB
Pss:               64652 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:     64652 kB
Referenced:        64652 kB
Anonymous:         64652 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me nr sd 
7fd693f23000-7fd694000000 ---p 00000000 00:00 0 
Size:                884 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: mr mw me nr sd 

再通過strace命令跟蹤了下系統呼叫,再回到上面的虛擬地址,我們找到了相關的mmap系統呼叫

[pid    71] 13:34:41.982589 mmap(0x7fd690000000, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fd690000000 <0.000107>

執行mmap的執行緒是71號執行緒,接著通過jstack把執行緒dump出來,找到了對應的執行緒其實是C2 CompilerThread0

"C2 CompilerThread0" #39 daemon prio=9 os_prio=0 tid=0x00007fd8acebb000 nid=0x47 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

最后再grep了一下strace的輸出,果然看到這個執行緒在大量的進行記憶體分配,總共有2G多,

經典的64M問題

對于64M的問題,是一個非常經典的問題,在JVM中并沒有這種大量分配64M大小的邏輯,因此可以排除JVM特定意義的分配,這其實是glibc里針對malloc函式分配記憶體的一種機制,glibc從2.10開始提供的一種機制,為了分配記憶體更加高效,glibc提供了arena的機制,默認情況下在64位下每一個arena的大小是64M,下面是64M的計算邏輯,其中sizeof(long)為8

define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long))
define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX)
p2 = (char *) MMAP (aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,
                          MAP_NORESERVE);

一個行程最多能分配的arena個數在64位下是8 * core,32位下是2 * core個

#define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8))
 {
              int n = __get_nprocs ();
              if (n >= 1)
                narenas_limit = NARENAS_FROM_NCORES (n);
              else
                /* We have no information about the system.  Assume two
                   cores.  */
                narenas_limit = NARENAS_FROM_NCORES (2);
            }

這種分配機制的好處,主要是應對多執行緒的環境,為每個核留有幾個64M的快取塊,這樣執行緒在分配記憶體的時候因為沒有鎖而變得更高效,如果達到上限了就會去慢速的main_arena里分配了,

可以通過設定環境變數MALLOC_ARENA_MAX來設定64M塊的個數,當我們設定為1的時候就會發現這些64M的記憶體塊都沒有了,然后都集中分配到一個大區域了,也就是main_arena,說明這個引數生效了,

無意的發現

再回過來思考為什么C2執行緒會出現大于2G的記憶體消耗的時候,無意中跟蹤C2這塊代碼發現了如下代碼可能會導致大量記憶體消耗,這個代碼的位置是nmethod.cpp的nmethod::metadata_do方法,不過這塊如果真的發生的話,肯定不是看到C2的執行緒大量分配,而是看到VMThread這個執行緒,因為下面這塊代碼主要是它執行的,

void nmethod::metadata_do(void f(Metadata*)) {
  address low_boundary = verified_entry_point();  if (is_not_entrant()) {
    low_boundary += NativeJump::instruction_size;    // %%% Note:  On SPARC we patch only a 4-byte trap, not a full NativeJump.
    // (See comment above.)  }  {    // Visit all immediate references that are embedded in the instruction stream.
    RelocIterator iter(this, low_boundary);    while (iter.next()) {
      if (iter.type() == relocInfo::metadata_type ) {
        metadata_Relocation* r = iter.metadata_reloc();        // In this metadata, we must only follow those metadatas directly embedded in
        // the code.  Other metadatas (oop_index>0) are seen as part of
        // the metadata section below.        assert(1 == (r->metadata_is_immediate()) +
               (r->metadata_addr() >= metadata_begin() && r->metadata_addr() < metadata_end()),               “metadata must be found in exactly one place”);
        if (r->metadata_is_immediate() && r->metadata_value() != NULL) {
          Metadata* md = r->metadata_value();          if (md != _method) f(md);
        }      } else if (iter.type() == relocInfo::virtual_call_type) {
        // Check compiledIC holders associated with this nmethod        CompiledIC *ic = CompiledIC_at(&iter);        if (ic->is_icholder_call()) {
          CompiledICHolder* cichk = ic->cached_icholder();          f(cichk->holder_metadata());          f(cichk->holder_klass());        } else {
          Metadata* ic_oop = ic->cached_metadata();          if (ic_oop != NULL) {
            f(ic_oop);          }        }      }    }  }inline CompiledIC* CompiledIC_at(RelocIterator* reloc_iter) {  assert(reloc_iter->type() == relocInfo::virtual_call_type ||
      reloc_iter->type() == relocInfo::opt_virtual_call_type, "wrong reloc. info");
  CompiledIC* c_ic = new CompiledIC(reloc_iter);  c_ic->verify();  return c_ic;
}

注意上面的CompiledIC *ic = CompiledIC_at(&iter);這段代碼,因為CompiledIC是一個ResourceObj,這種資源會在c heap里分配(malloc),不過他們是和執行緒進行關聯的,假如我們在某處代碼宣告了ResourceMark,那當執行到這里的時候會標記當前的位置,再接下來執行緒要分配記憶體的時候如果執行緒關聯的記憶體不夠用,就會malloc一塊插進去并被管理起來,否則會實作記憶體的復用,當ResourceMark解構式執行的時候,會將之前的位置還原,后面這個執行緒如果要分配記憶體又會從這個位置開始復用記憶體塊,注意這里說的記憶體塊和上面的64M記憶體塊不是一個概念,

因為這段代碼在while回圈里,因此存在非常多次數的重復呼叫,這樣明明在執行完一次之后可以復用記憶體的地方并不能復用,而可能會導致大量的記憶體被不斷分配,表現起來可能就是物理記憶體消耗很大,遠大于Xmx,

這個修復辦法也很簡單,就是在CompiledIC *ic = CompiledIC_at(&iter);前加上ResourceMark rm;即可,

這個問題主要發生的場景是針對頻繁大量做Class Retransform或者Class Redefine的場景,所以如果系統里有這種agent的時候還是要稍微注意下這個問題,

這個問題發現后我們給社區提了patch,不過后面發現再JDK12中其實已經修復了,但是在之前的版本里的都沒有修復,這個問題提交給社區后,有人很快回應了,并可能在OpenJDK1.8.0-212中被fix,

最后在這里也簡單提下客戶那邊的那個問題,之所以C2執行緒消耗太大,最主要的原因是存在非常大的方法需要編譯,而這個編譯的程序是需要大量的記憶體消耗的,正因為如此,才會導致記憶體突然暴增,所以給大家一個建議,方法不要寫太大啦,如果這個方法呼叫還很頻繁,那真的會很悲劇的,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/66114.html

標籤:其他

上一篇:Android studio 訊飛語音聽寫sdk集成

下一篇:【Java并發編程】volatile(一):執行緒安全三要素

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more