分析内存泄漏(二)

背景

之前有讲过内存分析 [http://www.91dengdeng.cn/2019/05/07/windbg分析内存泄漏 ]

最近刚好又碰到一个问题,方式差不多,但过程复杂了些。

更新了一个版本,在后台的监控程序中可以看到内存一直在上涨,差不多1小时 100M左右。

方法

  1. 保存了dump,按照之前的方式查看
1
2
3
4
5
6
7
8
9
10
11
12
0:000> !heap -s
SEGMENT HEAP ERROR: failed to initialize the extention
LFH Key : 0x52861f0b
Termination on corruption : DISABLED
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
Virtual block: 04360000 - 04360000 (size 00000000)
003b0000 00000002 1473664 1460924 1473664 9013 3856 96 1 0 LFH
005e0000 00001002 3136 1128 3136 604 7 4 0 3bd LFH
External fragmentation 53 % (7 free blocks)
00aa0000 00001002 256 4 256 1 1 1 0 0

堆块003b0000 提交了1.4G的内存

  1. 分析百分比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0:000> !heap -stat -h 003b0000 
heap @ 003b0000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
2c00 545f - e805400 (16.93)
ac17 9b7 - 687d371 (7.63)
a417 559 - 36d7eff (4.00)
a897 50f - 354d3d9 (3.89)
a7fb 493 - 3006121 (3.50)
a663 46b - 2df1761 (3.35)
9a17 49d - 2c6dc1b (3.24)
ae97 3c9 - 294d98f (3.01)
a817 3e4 - 28df97c (2.98)
ada3 26c - 1a486c4 (1.92)
2800 9d3 - 188f800 (1.79)
a797 221 - 164c877 (1.63)
a697 20a - 153afe6 (1.55)
a617 1e5 - 13aa993 (1.44)
aa23 19b - 1112631 (1.25)
aa17 193 - 10bc235 (1.22)
a517 19a - 10866d6 (1.21)
ae63 180 - 1059480 (1.19)
3000 518 - f48000 (1.12)
a7ef 165 - ea304b (1.07)

和之前不一样,没有哪块内存占用空间特别大,所以猜测应该是大量大小不一的内存导致的内存泄漏。

  1. 打印所有内存块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
0:000> !heap -stat -h 003b0000 -grp S 1000
heap @ 003b0000
group-by: TOTSIZE max-display: 4096
size #blocks total ( %) (percent of total busy bytes)
2c00 545f - e805400 (16.93)
ac17 9b7 - 687d371 (7.63)
a417 559 - 36d7eff (4.00)
a897 50f - 354d3d9 (3.89)
a7fb 493 - 3006121 (3.50)
a663 46b - 2df1761 (3.35)
9a17 49d - 2c6dc1b (3.24)
ae97 3c9 - 294d98f (3.01)
a817 3e4 - 28df97c (2.98)
ada3 26c - 1a486c4 (1.92)
2800 9d3 - 188f800 (1.79)
a797 221 - 164c877 (1.63)
a697 20a - 153afe6 (1.55)
a617 1e5 - 13aa993 (1.44)
aa23 19b - 1112631 (1.25)
aa17 193 - 10bc235 (1.22)
a517 19a - 10866d6 (1.21)
ae63 180 - 1059480 (1.19)
3000 518 - f48000 (1.12)
a7ef 165 - ea304b (1.07)
40 391f9 - e47e40 (1.04)
a987 146 - d7e1ea (0.98)
a9a3 13c - d16534 (0.96)
a5a3 142 - d05706 (0.95)
b097 12b - ce405d (0.94)
9617 150 - c4fe30 (0.90)
1000 c3d - c3d000 (0.89)
a763 12b - c380a1 (0.89)

--- 内容过长省略(总共有600行)

也就是说差不多有600个内存大小的申请,数量都不大,但是加起来有1.4G,该怎么找呢?

方法就是: 当内存上涨到 1.6G时,我们再打印一遍列表,然后比较1.4G和1.6G 两个的数量大小,比较下两者的差异,多出来的内存就是刚刚泄漏的内存。

结论

upload successful

我对比了下,找到了一些怀疑点,然后通过

1
!heap -flt s a397

获取内存的地址,并再memory中查看内存的内容,通过内存的内容找到了代码的怀疑点,并修复。

这个问题代码review时还真看不出来,UwlConstructRequest是第三方库,也没有函数说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  LPCONTEXT_HEAD	lpContext2 = NULL;
LPREQUEST lpRequest2 = NULL;
if (!UwlConstructRequest(buff.GetBuffer(), buff.GetBufferLen(), lpContext2, lpRequest2,
m_pbKey, m_lenKey, m_flagEncrypt, m_flagCompress, m_nPacketSize)) {
LOG_ERROR(_T("data package error. data abandoned."));
return FALSE;
}

REQUEST request;
memset(&request, 0, sizeof(REQUEST));
request.head.nRequest = UR_GATE_TRANSMIT;

UwlClearRequest(lpRequest2); // bug修复: 内存泄漏 。说明:UwlConstructRequest函数中申请了内存

SAFE_DELETE(lpContext2);
SAFE_DELETE(lpRequest2);