内存泄漏动态诊断
__all_virtual_memory_info 显示了整个 OBServer 通过 ObMalloc 分配内存的情况,每一块内存都有一个标签叫做 label 或者 mod_id,通过查看 mod 的统计信息,可以分析系统内存的使用情况,并初步确认疑似存在内存泄露的模块。
为了便于排查内存泄露问题,OceanBase 数据库实现了内存泄漏动态诊断机制,其工作原理是在每一次跟踪模块进行内存分配的时候,记录其申请内存的地址及相应的调用栈;在所申请内存被释放的时候,将该条记录清空。这样正常申请、释放内存的调用栈会被很快清空,而出现内存泄露的调用栈的记录将一直存在,最后我们按照调用栈进行分组,将每个不同的调用栈当前存在的申请记录进行累加,显示在 __all_mem_leak_checker_info 中。换言之,累计次数多的调用栈很可能出现了内存泄露(当然也有可能是由于各种缓存而造成的,这个需要具体问题具体分析)。
打开 memory_leak
obclient> alter system set leak_mod_to_check='OB_COMMON_ARRAY';
像这样,就指定了跟踪模块为 OB_COMMON_ARRAY,设置完毕后就可以开始监控 __all_mem_leak_checker_info 表的信息,查看可能出问题的调用栈。
查询
obclient> select * from __all_virtual_mem_leak_checker_info order by alloc_count desc;
一般来说存在泄露的调用栈计数都很大(而且越来越大),将这样的调用栈利用 addr2line 打印出。
addr2line -pCbfe bin/observer 0xe77df5 0xedf5a4 0xee14b0 0xee0923 1 0xeea558 0x4a05c3 0x1485cd9 0x1485223 0x1483c4b 0x1526f2a 0x1401359 0x1403075 0x140325a 0x1406416 0x14a51bb 0x14a5130 0x140x48f3aa7db8 0x14a4cc8 0x14a50f4 0x14a5cb1 0x130166e 0xd08bc9 0xd069d2 0xd01e97 0xeff033 0xefe6
$oceanbase_root/src/lib/utility/utility.cpp:58
$oceanbase_root/src/lib/../../src/lib/allocator/ob_mem_leak_checker.h:125
$oceanbase_root/src/lib/allocator/ob_tc_malloc.cpp:399
$oceanbase_root/src/lib/allocator/ob_tc_malloc.cpp:218
$oceanbase_root/src/observer/../../src/lib/allocator/ob_malloc.h:38
$oceanbase_root/src/lib/allocator/ob_malloc.cpp:121
$oceanbase_root/src/lib/../../src/lib/allocator/ob_malloc.h:116
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:295
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:291
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:468
$oceanbase_root/src/sql/optimizer/ob_log_table_scan.cpp:85
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1183
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1484
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1557
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:2092
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:467
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:455
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:890
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:378
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:450
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:568
$oceanbase_root/src/sql/optimizer/ob_optimizer.cpp:23
$oceanbase_root/src/sql/ob_sql.cpp:1160
$oceanbase_root/src/sql/ob_sql.cpp:887
$oceanbase_root/src/sql/ob_sql.cpp:152
$oceanbase_root/src/observer/mysql/obmp_query.cpp:263
关闭 memory_leak
打开 checker 后执行性能会显著下降,问题定位后及时将 checker 关闭。
obclient> alter system set leak_mod_to_check='';
memory_context 内存动态泄漏检查
对于长生命周期的内存管理,memory_leak 可以提供很好的诊断能力,方便找到调用栈定位问题。但对于短生命周期模型,因为内存总是会被很快释放,因此 memory_leak 往往无能为力,这就需要有一个新的功能来解决这个问题。
memory_context 是 OceanBase 数据库基于生命周期管理内存的接口,memory_context 自身就代表了内存的生命周期,因此可以扩展 memory_context 的能力,用它来实现对短周期内存泄漏的诊断,需要注意的是,memory_context 能保证即使有泄漏,内存也可以被完全释放干净,因此准确的说,我们支持的是"短生命周期内存的动态泄漏"。
找到 static_id
当日志打出了"HAS UNFREE"日志,找到日志里的 static_id。
[2020-10-22 15:30:57.923806] ERROR has_unfree_callback (object_set.cpp:27) [67779][462][Y40DC64589025-0005B23D7165EB9F] [lt=24] [dc=0] HAS UNFREE PTR!!!label: mytest1,static_id: 12,static_info: {filename_:"obmp_query.cpp", line_:475, function_:"process_single_stmt"}, dynamic_info: {tid_:67779, cid_:462, create_time_:1603351857840041}
打开 memory_leak
obclient> alter system set leak_mod_to_check='mytest1@12';
表示跟踪 static_id 为 12 的 memory_context 下的 mytest1 的内存申请,当然也支持通配符,但线上不建议这么做,毕竟有性能与空间代价,尽可能缩小范围才合理。
obclient> alter system set leak_mod_to_check='*@12';
这样写表示跟踪 static_id 为 12 的 memory_context 下的所有内存申请。
等待复现
下一次出现 HAS UNFREE 时,就会打出调用栈。
[2020-10-22 15:41:02.530881] INFO object_set.cpp:523 [67784][472][Y40DC64589025-0005B23D7135EBC2] [lt=15] [dc=0] CONTEXT MEMORY LEAK. ptr: 0x7f8f8a4145b0, size: 200, label: mytest1, lbt: 0xb979d8b 0xb72d239 0xb71ad7a 0x3a88b97 0x7fd735c 0x7fcee89 0x7fcd41d 0xbac5fad 0x8044c7a 0x80443b5 0x804001a 0x803f2ff 0x32e2cd7 0x32e2b7c 0x2d49d6d 0xb76f163 0xb76cd74 0xb76bdae
同样,可以用 addr2line 继续分析。
关闭 memory_leak
打开 checker 后执行性能会显著下降,问题定位后及时将 checker 关闭。
obclient> alter system set leak_mod_to_check='';
内存元数据 dump
可以把所有内存的元数据写到磁盘。
操作步骤
打开 OceanBase 数据库的部署目录。
新增
etc/dump.config文件, 写入目标指令(具体指令见下文)。kill -62 $pid,查看log/memory_meta文件。
指令汇总
输出所有内存信息
dump chunk all输出指定租户 +ctx 的内存信息
dump chunk tenant_id=$tenant_id,$ctx_id=ctx_id(注意是纯数字)
查看 glibc 接口申请的内存
malloc 等 glibc 内存申请会被透传到 OceanBase 数据库的内存管理接口,mod_id 为 glibc_malloc,可以通过以下方式查看:
obclient> select *from __all_virtual_memory_info where mod_name = 'glibc_malloc' and hold > 0;
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+
| tenant_id | svr_ip | svr_port | ctx_id | label | ctx_name | mod_type | mod_id | mod_name | zone | hold | used | count | alloc_count | free_count |
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+
| 500 | 100.81.113.215 | 46824 | 26 | glibc_malloc | GLIBC | user | 0 | glibc_malloc | z1 | 249800208 | 219156754 | 41247 | 0 | 0 |
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+



