欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用mtrace追蹤JVM堆外內存泄露的方法

 更新時間:2023年09月24日 15:48:30   作者:扣釘日記  
這篇文章主要給大家介紹了如何使用mtrace追蹤JVM堆外內存泄露,文章通過代碼示例介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下

mtrace追蹤內存泄露

glibc中提供了mtrace這個函數來開啟追蹤內存分配的功能,開啟后每次應用程序調用malloc或free函數時,會將內存分配釋放操作記錄在MALLOC_TRACE環(huán)境變量所指的文件里面,如下:

$ pid=`pgrep java`
# 配置gdb不調試信號,避免JVM收到信號后被gdb暫停
$ cat <<"EOF" > ~/.gdbinit
handle all nostop noprint pass
handle SIGINT stop print nopass
EOF
# 設置MALLOC_TRACE環(huán)境變量,將內存分配操作記錄在malloc_trace.log里
$ gdb -q -batch -ex 'call setenv("MALLOC_TRACE", "./malloc_trace.log", 1)' -p $pid
# 調用mtrace開啟內存分配追蹤
$ gdb -q -batch -ex 'call mtrace()' -p $pid
# 一段時間后,調用muntrace關閉追蹤
$ gdb -q -batch -ex 'call muntrace()' -p $pid

然后查看malloc_trace.log,內容如下:

可以發(fā)現,在開啟mtrace后,glibc將所有malloc、free操作都記錄了下來,通過從日志中找出哪些地方執(zhí)行了malloc后沒有free,即是內存泄露點。

于是glibc又提供了一個mtrace命令,其作用就是找出上面說的執(zhí)行了malloc后沒有free的記錄,如下:

$ mtrace malloc_trace.log | less -n
Memory not freed:
-----------------
           Address     Size     Caller
0x00007efe08008cc0     0x18  at 0x7efe726e8e5d
0x00007efe08008ea0    0x160  at 0x7efe726e8e5d
0x00007efe6cabca40     0x58  at 0x7efe715dc432
0x00007efe6caa9ad0   0x1bf8  at 0x7efe715e4b88
0x00007efe6caab6d0   0x1bf8  at 0x7efe715e4b88
0x00007efe6ca679c0   0x8000  at 0x7efe715e4947
# 按Caller分組統(tǒng)計一下,看看各Caller各泄露的次數及內存量
$ mtrace malloc_trace.log | sed '1,/Caller/d'|awk '{s[$NF]+=strtonum($2);n[$NF]++;}END{for(k in s){print k,n[k],s[k]}}'|column -t
0x7efe715e4b88  1010  7231600
0x7efe715dc432  1010  88880
0x7efe715e4947  997   32669696
0x7efe726e8e5d  532   309800
0x7efe715eb2f4  1     72
0x7efe715eb491  1     38

可以發(fā)現,0x7efe715e4b88這個調用點,泄露了1010次,那怎么知道這個調用點在哪個函數里呢?

根據指令地址找函數

之前我們介紹過Linux進程的虛擬內存布局,如下:

  • Stack:棧,向下擴展,為線程分配的棧內存。
  • Memory Mapping Segment:內存映射區(qū)域,通過mmap分配,如映射的*.so動態(tài)庫、動態(tài)分配的匿名內存等。
  • Heap:堆,向上擴展,動態(tài)分配內存的區(qū)域。
  • Data Segment:數據段,一般用來存儲如C語言中的全局變量。
  • Code Segment:代碼段,對于JVM來說,它從bin/java二進制文件加載而來。

而對于JVM來說,bin/java只是一個啟動進程的殼,真正的代碼基本都在動態(tài)庫中,如libjvm.so、libzip.so等。

而在Linux中,動態(tài)庫都是直接加載的,如下:

因此,通過如下步驟,即可知道某個指令地址來自哪個函數,如下:

  • 根據指令地址,找到其所屬的動態(tài)庫,以及動態(tài)庫在進程虛擬內存空間中的起始地址。
  • 根據指令地址減去起始地址,算出指令在動態(tài)庫中的偏移量地址。
  • 反匯編動態(tài)庫文件,根據偏移量地址查找指令所在函數。
  • 找動態(tài)庫及起始地址
$ pmap -x $pid -p -A 0x7efe715e4b88
Address           Kbytes     RSS   Dirty Mode  Mapping
00007efe715d9000     108     108       0 r-x-- /opt/jdk8u222-b10/jre/lib/amd64/libzip.so
---------------- ------- ------- -------
total kB             108  163232  160716

通過pmap的-A選項,可以通過內存地址找內存映射區(qū)域,如上,Mapping列就是內存映射區(qū)域對應的動態(tài)庫文件,而Address列是其在進程虛擬內存空間中的起始地址。

  • 計算指令在動態(tài)庫中的偏移量
# 指令地址減去動態(tài)庫起始地址
$ printf "%x" $((0x7efe715e4b88-0x00007efe715d9000))
bb88
  • 反匯編并查找指令
$ objdump -d /opt/jdk8u222-b10/jre/lib/amd64/libzip.so | less -n

可以發(fā)現,進程地址0x7efe715e4b88上的指令,在inflateInit2_函數中。

當然,上面步驟有點復雜,其實也可以通過gdb來查,如下:

gdb -q -batch -ex 'info symbol 0x7efe715e4b88' -p $pid

這樣,我們找到了泄露的原生函數名,那是什么java代碼調用到這個函數的呢?

通過原生函數名找Java調用棧

通過arthas的profiler命令,可以采樣到原生函數的調用棧,如下:

[arthas@1]$ profiler execute 'start,event=inflateInit2_,alluser'
Profiling started
[arthas@1]$ profiler stop
OK
profiler output file: .../arthas-output/20230923-173944.html

打開這個html文件,可以發(fā)現相關的Java調用棧,如下:

外內存泄露的代碼路徑就找到了,只需要再看看代碼,識別一下哪些代碼路徑確實會導致內存泄露即可。

注:經過測試,發(fā)現profiler其實可以直接使用指令地址,所以不轉換為函數名稱,也是OK的。

通過jna開啟mtrace

gdb實際是C/C++的調試程序,通過gdb來直接調用native函數,可能會出現一些不確定因素。

眾所周知,Java提供了JNI機制,可實現Java調用native函數,而jna(Java Native Access)則對JNI技術進行了封裝,大大簡化了Java調用native函數的開發(fā)工作。

因此,我們可以使用jna來調用mtrace等native函數,如下:

  • 引入jna庫
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.2.2</version>
</dependency>
  • 封裝并調用native函數
public class JnaTool {
    public interface CLibrary extends Library {
        void malloc_stats();
        void malloc_trim(int pad);
        void setenv(String name, String value, int overwrite);
        void mtrace();
        void muntrace();
    }
    private static CLibrary cLibrary;
    static {
        try {
            cLibrary = (CLibrary) Native.loadLibrary("c", CLibrary.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void mtrace(String traceFile) {
        if (cLibrary == null) return;
        cLibrary.setenv("MALLOC_TRACE", traceFile, 1);
        cLibrary.mtrace();
    }
    public static void muntrace() {
        if (cLibrary == null) return;
        cLibrary.muntrace();
    }
    public static void mallocStats() {
        if (cLibrary == null) return;
        cLibrary.malloc_stats();
    }
    public static void mallocTrim() {
        if (cLibrary == null) return;
        cLibrary.malloc_trim(0);
    }
}

這樣,就可以避免使用gdb而調用一些C庫函數了

以上就是使用mtrace追蹤JVM堆外內存泄露的方法的詳細內容,更多關于mtrace追蹤JVM內存泄露的資料請關注腳本之家其它相關文章!

相關文章

  • java 靜態(tài)工廠代替多參構造器的適用情況與優(yōu)劣

    java 靜態(tài)工廠代替多參構造器的適用情況與優(yōu)劣

    這篇文章主要介紹了java 靜態(tài)工廠代替多參構造器的優(yōu)劣,幫助大家更好的理解和使用靜態(tài)工廠方法,感興趣的朋友可以了解下
    2020-12-12
  • Java Springboot如何基于圖片生成下載鏈接

    Java Springboot如何基于圖片生成下載鏈接

    這篇文章主要介紹了Java Springboot如何基于圖片生成下載鏈接,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • Java 完美判斷中文字符的方法

    Java 完美判斷中文字符的方法

    Java判斷一個字符串是否有中文一般情況是利用Unicode編碼正則來做判斷,但是其實這個區(qū)間來判斷中文不是非常精確,以下是比較完善的判斷方法
    2013-02-02
  • java顯示當前運行時的參數(java運行參數)

    java顯示當前運行時的參數(java運行參數)

    這篇文章主要介紹了java顯示當前運行時參數的示例(java運行參數),需要的朋友可以參考下
    2014-04-04
  • Java中RocketMQ使用方法詳解

    Java中RocketMQ使用方法詳解

    這篇文章主要介紹了RocketMQ和Kafka在SpringBoot中的使用方法,以及如何保證消息隊列的順序性、可靠性以及冪等性,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-02-02
  • spring boot 實現配置多個DispatcherServlet最簡單方式

    spring boot 實現配置多個DispatcherServlet最簡單方式

    這篇文章主要介紹了spring boot 實現配置多個DispatcherServlet最簡單方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 使用redis的increment()方法實現計數器功能案例

    使用redis的increment()方法實現計數器功能案例

    這篇文章主要介紹了使用redis的increment()方法實現計數器功能案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Java中的JVM虛擬機內存分配詳解

    Java中的JVM虛擬機內存分配詳解

    這篇文章主要介紹了Java中的JVM虛擬機內存分配詳解,虛擬機是一種能夠執(zhí)行 Java 字節(jié)碼的虛擬機,它是 Java 語言的核心組成部分,負責將 Java 代碼轉換為機器碼并執(zhí)行,JVM 提供了內存管理、垃圾回收、線程管理等功能,需要的朋友可以參考下
    2023-10-10
  • SpringBoot使用@Validated處理校驗的方法步驟

    SpringBoot使用@Validated處理校驗的方法步驟

    @Validated?注解的主要目的是啟用和利用?Spring?的驗證框架,它可以用于類上也可以用于方法參數上,本文給大家介紹了SpringBoot使用@Validated優(yōu)雅的處理校驗的方法步驟,通過代碼示例介紹的非常詳細,需要的朋友可以參考下
    2024-08-08
  • IDEA 如何導入別人的javaweb項目進行部署

    IDEA 如何導入別人的javaweb項目進行部署

    這篇文章主要介紹了IDEA 如何導入別人的javaweb項目進行部署,本文給大家分享我的詳細部署過程及遇到問題解決方法,需要的朋友可以參考下
    2023-03-03

最新評論