Valgrind による C/C++ メモリリーク検出
[履歴] [最終更新] (2019/10/23 22:43:28)

概要

JVM メモリリークでは JDK の jstat や jmap で原因を調査できます。C/C++ では valgrindMemcheck ツールが利用できます。valgrind には複数のツールが含まれており既定のツールが Memcheck です。他のツールを利用する場合は --tool オプションで指定します。

簡単な利用例definitely lost

必須ではありませんが -g -O0 オプションでコンパイルすると、valgrind の出力にファイル名および行番号が含まれるようになります。

main.cpp

#include <iostream>

int main() {
    int* arr = new int[10];
    arr[10] = 1;
    return 0;
}

実行例

g++ -Wall -O0 -g main.cpp
valgrind --leak-check=yes a.out

出力例

==21582== Memcheck, a memory error detector
==21582== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==21582== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==21582== Command: a.out
==21582== 
==21582== Invalid write of size 4
==21582==    at 0x109173: main (main.cpp:5)
==21582==  Address 0x4d59ca8 is 0 bytes after a block of size 40 alloc'd
==21582==    at 0x483650F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==21582==    by 0x109166: main (main.cpp:4)
==21582== 
==21582== 
==21582== HEAP SUMMARY:
==21582==     in use at exit: 40 bytes in 1 blocks
==21582==   total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated
==21582== 
==21582== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==21582==    at 0x483650F: operator new[](unsigned long) (vg_replace_malloc.c:423)
==21582==    by 0x109166: main (main.cpp:4)
==21582== 
==21582== LEAK SUMMARY:
==21582==    definitely lost: 40 bytes in 1 blocks
==21582==    indirectly lost: 0 bytes in 0 blocks
==21582==      possibly lost: 0 bytes in 0 blocks
==21582==    still reachable: 0 bytes in 0 blocks
==21582==         suppressed: 0 bytes in 0 blocks
==21582== 
==21582== For counts of detected and suppressed errors, rerun with: -v
==21582== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
  • ==21582== → プロセス ID です。
  • Invalid write of size 4 → 無効なインデックス 10 arr[10] = 1 が指定されたことが検出されました。
  • definitely lostarr を free (delete) していないことが検出されました。

indirectly lost

メモリブロックへの参照 ptr->ptr は存在している場合であっても ptr 自体が lost してしまった場合は、結果として ptr->ptr で参照するメモリブロックは利用できなくなります。このような場合のメモリリークは indirectly lost として検出されます。

#include <iostream>

struct Node {
    Node* ptr;
};

int main() {
    Node* ptr = new Node();
    ptr->ptr = new Node();
    return 0;
}

出力例

$ valgrind --leak-check=yes a.out
==27212== Memcheck, a memory error detector
==27212== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27212== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==27212== Command: a.out
==27212== 
==27212== 
==27212== HEAP SUMMARY:
==27212==     in use at exit: 16 bytes in 2 blocks
==27212==   total heap usage: 3 allocs, 1 frees, 72,720 bytes allocated
==27212== 
==27212== 16 (8 direct, 8 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==27212==    at 0x4835DEF: operator new(unsigned long) (vg_replace_malloc.c:334)
==27212==    by 0x109166: main (main.cpp:8)
==27212== 
==27212== LEAK SUMMARY:
==27212==    definitely lost: 8 bytes in 1 blocks
==27212==    indirectly lost: 8 bytes in 1 blocks
==27212==      possibly lost: 0 bytes in 0 blocks
==27212==    still reachable: 0 bytes in 0 blocks
==27212==         suppressed: 0 bytes in 0 blocks
==27212== 
==27212== For counts of detected and suppressed errors, rerun with: -v
==27212== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

still reachable

プログラムが終了する前に free/delete されなかった領域が存在する場合に still reachable と出力されます。プログラム自体が終了しているため definitely lost の場合と異なりメモリリークとして問題になることはありません。

#include <iostream>

int main() {
    int* arr = new int[10];
    exit(1);
    return 0;
}

出力例

$ valgrind --leak-check=yes a.out
==27963== Memcheck, a memory error detector
==27963== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27963== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==27963== Command: a.out
==27963== 
==27963== 
==27963== HEAP SUMMARY:
==27963==     in use at exit: 40 bytes in 1 blocks
==27963==   total heap usage: 2 allocs, 1 frees, 72,744 bytes allocated
==27963== 
==27963== LEAK SUMMARY:
==27963==    definitely lost: 0 bytes in 0 blocks
==27963==    indirectly lost: 0 bytes in 0 blocks
==27963==      possibly lost: 0 bytes in 0 blocks
==27963==    still reachable: 40 bytes in 1 blocks
==27963==         suppressed: 0 bytes in 0 blocks
==27963== Reachable blocks (those to which a pointer was found) are not shown.
==27963== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==27963== 
==27963== For counts of detected and suppressed errors, rerun with: -v
==27963== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
関連ページ
    概要 メモリリーク時に JVM の jmap や C/C++ の Valgrind で調査できるのと同様に、Python では objgraph が便利です。 sudo apt install graphviz python -m pip install xdot python -m pip install objgraph