서버 프로그램을 할때 가장 중요한 요소중 하나는 Memory leak 과 같은 현상에 대한 대비를 철저히 해야한다는 점입니다. Device Driver 의 경우 Pool Tagging이나 Driver Verifier를 통해서 이러한 현상을 비교적 쉽게 (??)해결 할 수 있지요 . 하지만 어플리케이션 서버 프로그램에서는 쉽지 않은 과제 입니다. 특히나 IOCP를 이용한 서버 프로그램의 경우 Page Heap 을 이용하여 디버깅이 쉽지 않기 때문에 더더욱 그렇지요 ( IOCP를 이용하여 프로그램 할 경우 APC( APC Delivery)를 통해서 동작하기 때문에 DPH Block 관련 Critical Section과 Deadlock이 발생할 수 있습니다. )
다행이도 Windbg에서는 굉자히 쉽게 이러한 Memory Leak을 찾아주는 Command가 있지요 . !heap -l 이라는 녀석을 통해서 보면 각각의 CRT Heap , Process Heap 등으로 부터 Leak으로 생각되는 부분을 손쉽게 검색해줍니다. 물론 시간을 조금 걸립니다. ( 제 PC는 별로 좋지 않아서 30분이상 소요되는 경우도 많습니다. )
0:032> !heap -l
Searching the memory for potential unreachable busy blocks.
Heap 00140000
Heap 00240000
Heap 00370000
Heap 00390000
Heap 003c0000
Heap 00920000
Heap 003e0000
Heap 00b20000
Heap 00b60000
Heap 00ba0000
Heap 00be0000
Heap 00c20000
Heap 00c60000
Heap 00ca0000
Heap 00d00000
Heap 00d40000
Heap 00d50000
Heap 011b0000
Heap 011f0000
Heap 01200000
Heap 01240000
Heap 011a0000
Heap 01420000
ERROR: Block 08e93908 previous size c60b does not match previous block size 10b
HEAP 01420000 (Seg 08d60000) At 08e93908 Error: invalid block Previous
ERROR: Block 0ad07b38 previous size 800b does not match previous block size 10b
HEAP 01420000 (Seg 0a160000) At 0ad07b38 Error: invalid block Previous
Heap 01450000
Heap 01490000
Heap 014d0000
Heap 01510000
Heap 01550000
Heap 01590000
Heap 015d0000
Heap 01610000
Heap 01a80000
Heap 01b90000
Heap 01c30000
Heap 01d30000
Heap 01d50000
Heap 01d90000
Heap 01e00000
Heap 01e40000
Heap 024c0000
Heap 02500000
Heap 02540000
Heap 02580000
Heap 025c0000
Heap 02600000
Heap 02640000
Heap 02680000
Heap 026c0000
Heap 02700000
Heap 01de0000
Heap 073c0000
Heap 07a00000
Heap 07a40000
Heap 08880000
Heap 088c0000
Heap 08900000
Heap 00a70000
Scanning VM …
Scanning references from 922087 busy blocks (1792 MBytes) …
Entry User Heap Segment Size PrevSize Unused Flags
—————————————————————————–
00140000 00140008 00140000 00140000 640 0 0 busy
00140640 00140648 00140000 00140000 40 640 0 busy
00140680 00140688 00140000 00140000 1808 40 8 busy
00141e88 00141e90 00140000 00140000 20 1808 8 busy
00141ea8 00141eb0 00140000 00140000 58 20 8 busy
00141f00 00141f08 00140000 00140000 58 58 8 busy
00141f58 00141f60 00140000 00140000 210 58 8 busy
00142168 00142170 00140000 00140000 228 210 e busy
00142390 00142398 00140000 00140000 10 228 8 busy
001423a0 001423a8 00140000 00140000 a8 10 c busy
…
2058c9a0 2058c9a8 01420000 20500000 858 858 8 busy
2058d1f8 2058d200 01420000 20500000 858 858 8 busy
2058da50 2058da58 01420000 20500000 858 858 8 busy
2058e2a8 2058e2b0 01420000 20500000 858 858 8 busy
2058eb00 2058eb08 01420000 20500000 858 858 8 busy
2058f358 2058f360 01420000 20500000 858 858 8 busy
2058fbb0 2058fbb8 01420000 20500000 858 858 8 busy
20590408 20590410 01420000 20510000 858 858 8 busy
20590c60 20590c68 01420000 20510000 858 858 8 busy
205914b8 205914c0 01420000 20510000 858 858 8 busy
20591d10 20591d18 01420000 20510000 858 858 8 busy
20592568 20592570 01420000 20510000 858 858 8 busy
20592dc0 20592dc8 01420000 20510000 858 858 8 busy
20593618 20593620 01420000 20510000 858 858 8 busy
20593e70 20593e78 01420000 20510000 858 858 8 busy
205946c8 205946d0 01420000 20510000 858 858 8 busy
20594f20 20594f28 01420000 20510000 858 858 8 busy
20595778 20595780 01420000 20510000 858 858 8 busy
20595fd0 20595fd8 01420000 20510000 858 858 8 busy
20596828 20596830 01420000 20510000 858 858 8 busy
20597080 20597088 01420000 20510000 858 858 8 busy
205978d8 205978e0 01420000 20510000 858 858 8 busy
20598130 20598138 01420000 20510000 858 858 8 busy
20598988 20598990 01420000 20510000 858 858 8 busy
205991e0 205991e8 01420000 20510000 858 858 8 busy
20599a38 20599a40 01420000 20510000 858 858 8 busy
2059a290 2059a298 01420000 20510000 858 858 8 busy
2059aae8 2059aaf0 01420000 20510000 858 858 8 busy
2059b340 2059b348 01420000 20510000 858 858 8 busy
2059bb98 2059bba0 01420000 20510000 858 858 8 busy
2059c3f0 2059c3f8 01420000 20510000 858 858 8 busy
2059cc48 2059cc50 01420000 20510000 858 858 8 busy
2059d4a0 2059d4a8 01420000 20510000 858 858 8 busy
2059dcf8 2059dd00 01420000 20510000 858 858 8 busy
2059e550 2059e558 01420000 20510000 858 858 8 busy
2059eda8 2059edb0 01420000 20510000 858 858 8 busy
2059f600 2059f608 01420000 20510000 858 858 8 busy
2059fe58 2059fe60 01420000 20510000 858 858 8 busy
205a06b0 205a06b8 01420000 20520000 858 858 8 busy
205a0f08 205a0f10 01420000 20520000 858 858 8 busy
205a1760 205a1768 01420000 20520000 858 858 8 busy
205a1fb8 205a1fc0 01420000 20520000 858 858 8 busy
205a2810 205a2818 01420000 20520000 858 858 8 busy
205a3068 205a3070 01420000 20520000 858 858 8 busy
205a38c0 205a38c8 01420000 20520000 858 858 8 busy
205a4118 205a4120 01420000 20520000 858 858 8 busy
205a4970 205a4978 01420000 20520000 858 858 8 busy
205a51c8 205a51d0 01420000 20520000 858 858 8 busy
205a5a20 205a5a28 01420000 20520000 858 858 8 busy
205a6278 205a6280 01420000 20520000 858 858 8 busy
205a6ad0 205a6ad8 01420000 20520000 858 858 8 busy
205a7328 205a7330 01420000 20520000 858 858 8 busy
205a7b80 205a7b88 01420000 20520000 858 858 8 busy
205a83d8 205a83e0 01420000 20520000 858 858 8 busy
205a8c30 205a8c38 01420000 20520000 858 858 8 busy
205a9488 205a9490 01420000 20520000 858 858 8 busy
205a9ce0 205a9ce8 01420000 20520000 858 858 8 busy
205aa538 205aa540 01420000 20520000 858 858 8 busy
205aad90 205aad98 01420000 20520000 858 858 8 busy
205ab5e8 205ab5f0 01420000 20520000 858 858 8 busy
205abe40 205abe48 01420000 20520000 858 858 8 busy
205ac698 205ac6a0 01420000 20520000 858 858 8 busy
205acef0 205acef8 01420000 20520000 858 858 8 busy
205ad748 205ad750 01420000 20520000 858 858 8 busy
205adfa0 205adfa8 01420000 20520000 858 858 8 busy
…
7505eed8 7505eee0 01420000 74cf0000 858 858 8 busy
7505f730 7505f738 01420000 74cf0000 858 858 8 busy
7505ff88 7505ff90 01420000 74cf0000 858 858 8 busy
750607e0 750607e8 01420000 74d00000 858 858 8 busy
75061038 75061040 01420000 74d00000 858 858 8 busy
75061890 75061898 01420000 74d00000 858 858 8 busy
750620e8 750620f0 01420000 74d00000 858 858 8 busy
75062940 75062948 01420000 74d00000 858 858 8 busy
75063198 750631a0 01420000 74d00000 858 858 8 busy
750639f0 750639f8 01420000 74d00000 858 858 8 busy
75064248 75064250 01420000 74d00000 858 858 8 busy
75064aa0 75064aa8 01420000 74d00000 858 858 8 busy
750652f8 75065300 01420000 74d00000 858 858 8 busy
75065b50 75065b58 01420000 74d00000 858 858 8 busy
750663a8 750663b0 01420000 74d00000 858 858 8 busy
75066c00 75066c08 01420000 74d00000 858 858 8 busy
75067458 75067460 01420000 74d00000 858 858 8 busy
75067cb0 75067cb8 01420000 74d00000 858 858 8 busy
75068508 75068510 01420000 74d00000 858 858 8 busy
…
922087 potential unreachable blocks were detected.
이 Dump File ( Application 강제 Dump 입니다. )에서는 굉장히 많은 Block 이 Leak으로 발생하고 있습니다. 858이라는 사이즈로 말이죠.
0:032> dd 2058c9a0
2058c9a0 010b010b 080801d2 00000000 00000000
2058c9b0 00000000 00000000 00000000 2058c164
2058c9c0 2058d214 0142427c 00000000 00000004
2058c9d0 000000d7 00000800 2058c9ec 0142426c
2058c9e0 00000001 00000800 00000005 00000001
2058c9f0 00000063 00000000 00000000 00000000
2058ca00 00000000 00000000 00000000 00000000
2058ca10 00000000 00000000 00000000 00000000
0:032> dd 7505eed8
7505eed8 010b010b 3608013d 00000000 00000000
7505eee8 00000000 00000000 00000000 7505e69c
7505eef8 7505f74c 0142427c 00000000 00000004
7505ef08 00000800 00000800 7505ef24 0142426c
7505ef18 00000001 00000800 00000005 00000001
7505ef28 00000063 00000000 00000000 00000000
7505ef38 00000000 00000000 00000000 00000000
7505ef48 00000000 00000000 00000000 00000000
이러한 어려운 상황에서 Memory Leak이 발생한 곳을 찾아 가기 위해서는 랜덤하게 여러개의 Block의 메모리를 열어 보면서 공통이 되는 부분을 찾는 것이 가장 중요합니다. 물론 Leak이 발생한 Block이 어떠한 String을 담고 있다면 이보다 좋은 경우는 없습니다. ( 위의 경우는 Memory Block에 스트링이 존재하지 안습니다. ) 두개의 Block을 비교해 보면 공통적으로 가지는 데이터가 있습니다. 이 점에서 추정할 수 있는 것은 이 메모리는 Struct 나 Class를 할당 한 것으로 추정이 가능합니다. 그리고 파란색으로 표신된 Address의 경우 Open된 Memory와 비슷한 영역에 할당된것으로 보여지는 Address값이 들어 있습니다.
0:032> !address 7505e69c
75000000 : 75000000 - 001be000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsag
0:032> !address 7505f74c
75000000 : 75000000 - 001be000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageHeap
Handle 01420000
역시 같은 Heap Handle을 가지는 영역에 할당된 메모리군요. 이 점에서 두개의 메모리값이 연속적으로 나타난다는 것은 이 메모리가 Double Linked List로 연결되어있지 않을까 하고 생각해 볼 수 있죠 .
0:032> dl 7505e69c
7505e69c 7505de44 7505eef4 0142427c 00000000
7505de44 7505d5ec 7505e69c 0142427c 00000000
7505d5ec 7505cd94 7505de44 0142427c 00000000
7505cd94 7505c53c 7505d5ec 0142427c 00000000
7505c53c 7505bce4 7505cd94 0142427c 00000000
7505bce4 7505b48c 7505c53c 0142427c 00000000
7505b48c 7505ac34 7505bce4 0142427c 00000000
7505ac34 7505a3dc 7505b48c 0142427c 00000000
7505a3dc 75059b84 7505ac34 0142427c 00000000
75059b84 7505932c 7505a3dc 0142427c 00000000
7505932c 75058ad4 75059b84 0142427c 00000000
75058ad4 7505827c 7505932c 0142427c 00000000
7505827c 75057a24 75058ad4 0142427c 00000000
75057a24 750571cc 7505827c 0142427c 00000000
750571cc 75056974 75057a24 0142427c 00000000
75056974 7505611c 750571cc 0142427c 00000000
7505611c 750558c4 75056974 0142427c 00000000
750558c4 7505506c 7505611c 0142427c 00000000
7505506c 75054814 750558c4 0142427c 00000000
75054814 75053fbc 7505506c 0142427c 00000000
75053fbc 75053764 75054814 0142427c 00000000
75053764 75052f0c 75053fbc 0142427c 00000000
75052f0c 750526b4 75053764 0142427c 00000000
750526b4 75051e5c 75052f0c 0142427c 00000000
75051e5c 75051604 750526b4 0142427c 00000000
75051604 75050dac 75051e5c 0142427c 00000000
75050dac 75050554 75051604 0142427c 00000000
75050554 7504fcfc 75050dac 0142427c 00000000
7504fcfc 7504f4a4 75050554 0142427c 00000000
7504f4a4 7504ec4c 7504fcfc 0142427c 00000000
7504ec4c 7504e3f4 7504f4a4 0142427c 00000000
7504e3f4 7504db9c 7504ec4c 0142427c 00000000
Memory의 뒷 부분에 공통데이터를 가지는 것으로 보아 Leak이 발생한 Memory는 Double Linked List로 연결된 것으로 추정이 가능합니다.
이제 정리해 보면 Memory leak이 발생한 Block은 0×858정도의 Size를 가지며 몇가지 공통 데이터를 가지고 있고 결정적으로 Double Linked List를 포함하는 Struct나 Class가 Memory Leak을 발생시키는 것으로 추측이 가능하죠 .
코드를 보면서 이조건에 맞는 녀석을 검색하면 답은 나오겠군요 ^ ^
최근 답글