gc内存快照
简介
用于统计driver运行时内存占用情况的工具。
mem_snapshot 方法统计不同snapshot下的handle数量的相对改变,缺点是无法获取具体的内存大小仅能获得某类成员的相对数量。
mem_record则是记录某一内存申请位置已申请但未释放的内存大小,缺点是只能记录内存申请位置无法知晓已申请内容具体被哪个 handle 引用,需要自己分析代码。
gc_snapshot 是记录所有内存指针的 mark from 信息,最终得到各个 handle 引用的内存大小,缺点是无法获得具体的引用路径。
gc内存快照
在保证操作系统剩余内存空间大于 driver 1/4 page-alloc 内存大小的情况下可以直接通过'gc_snapshot()收集并获得各个 handle 引用的内存大小。
// mem_profiler.gs
public map gc_snapshot(int max_round = 3, int element_limit = 4, int layer_limit = 3, int describ_limit = 66)
"max_round": Maxim round of gc.search(...) works
"m_element_limit": Limit of element number for generated container describtion.
"m_layer_limit": Limit of depth for gernated container describtion.
"describ_limit": Limit of output unit desribtion str
简单的使用样例:
import gs.mem_profiler;
array test_arr = [];
for(int i = 0; i < 10000; i++)
{
test_arr.push_back(get_system_info());// Internal mem_profiler will record the memory alloc place
}
write(mem_profiler.gc_snapshot());
结果如下:
{ /* sizeof() == 2 */
"total_size" : 78121024,
"mem_report" : [ /* sizeof() == 126 */
"object[2927376:v0]/test/test.gs<Static> size:77243488",
"pseudo[8960:v0]string_pool size:375168",
"<unknown> size:252960",
"<maybe-root> size:107776",
"pseudo[11520:v0]permanent_pool size:52768",
"co[440664:v0]boot size:8992",
"co[440624:v0]Shell size:3168",
"object[3026432:v0]/gs/jit_warmup.gs<Default> size:2816",
"object[11552:v0]/builtin/ffi/ffi_type.gs<Parallel> size:2592",
"program[2955092:v0]/gs/mem_profiler.gs size:2464",
"program[2927756:v0]/gs/util/md5.gs size:2208",
"{"BOOL_MIN":0, "BOOL_MAX":1, "CHAR_MIN":-128, "CHAR_MAX":127, ...sksize:1920",
"program[2735784:v0]/builtin/ffi/ffi_type.gs size:1856",
"program[2735840:v0]/builtin/ffi/gsffi_parser.gs size:1824",
"co[440584:v0]ob_post_2 size:1824",
"object[2849792:v0]/builtin/shell.gs<Parallel> size:1696",
"co[2951424:v0]hot_spot_detect size:1600",
注意事项
上述测例中可以看到 test.gs 通过 object 变量 test_arr 持有了77MB左右的内存,但还有另一种情况,如下:
import gs.mem_profiler;
void test()
{
array local_test_arr = [];
for(int i = 0; i < 10000; i++)
{
local_test_arr.push_back(get_system_info());
}
write(mem_profiler.gc_snapshot());
}
test();
不同于之前的例子,在调用gc_snapshot时大量内存是被函数内的局部变量 local_test_arr 存储的,而局部变量实际是在 Coroutine 的 Value Stack 中存储的,所以最终内存会被统计到执行 test 函数的 croutine 上。
结果如下:
{ /* sizeof() == 2 */
"total_size" : 78132864,
"mem_report" : [ /* sizeof() == 130 */
"co[440664:v0]boot size:77251200",
"pseudo[8960:v0]string_pool size:369664",
"<unknown> size:254560",
"<maybe-root> size:89536",
"pseudo[11520:v0]permanent_pool size:56672",
"co[440624:v0]Shell size:24672",
"{"version":"1.23.240729", "run_in_main_thread":0, "first_execution"size:6848",
"program[2942804:v0]/gs/mem_profiler.gs size:3744",
"object[11552:v0]/builtin/ffi/ffi_type.gs<Parallel> size:2656",
"program[2915468:v0]/gs/util/md5.gs size:2208",
"program[2735784:v0]/builtin/ffi/ffi_type.gs size:2112",
"{"BOOL_MIN":0, "BOOL_MAX":1, "CHAR_MIN":-128, "CHAR_MAX":127, ...sksize:1920",
"co[2939176:v0]jit_compile size:1856",
"program[2735840:v0]/builtin/ffi/gsffi_parser.gs size:1824",
"object[5634560:v7]/builtin/context.gs size:1728",
"pseudo[11524:v0]root_values size:1632",
"{"telnet_port":"2099", "telnet_password":"md5:5ed33f7008771c9d49e37size:1504",
"<unknown>" 代表相应的内存指针未被引用到
"<maybe-root>" 代表被gc根节点引用
gc_snapshot 会尽可能保证绝大多数内存指针的引用记录都是来自 handle 类型,少部分则直接生成引用记录unit的描述信息。