Finalizer 쓰레드에 의한 메모리 누수 현상

Finalizer Thread CLR 메모리 덤프에서 볼 수 있는 특이한 Thread 중에 하나이다. 오브젝트가 Garbage Collected 될 때, 오브젝트의 Finalize Method Finalizer에 의해서 수행이 된다. 일반적으로 이러한 Finalization이 필요한 오브젝트는 File 이나 Unmanaged Memory와 같은 Unmanaged Resource를 사용하는 오브젝트이며, 필요에 의해 Finalize Method를 구현하여 이와 같은 Unmanaged resource의 누수를 방지하는 역할을 하는 것이 Finalizer Thread이다. 일반적으로 Finalizer Thread를 확인하는 방법은 !threads의 출력으로 다음과 같이 확인할 수 있다.

 

0:026> !threads

ThreadCount: 12

UnstartedThread: 0

BackgroundThread: 12

PendingThread: 0

DeadThread: 0

                                  PreEmptive   GC Alloc                     Lock    

        ID  ThreadOBJ       State     GC       Context           Domain     Count APT Exception

 16  0xf44 0x000d23e0      0xa220 Enabled  0x06953ce0:0x06954ff4 0x00113b98     1 MTA

 20  0x7bc 0x000d3910      0xb220 Enabled  0x00000000:0x00000000 0x000cd460     0 MTA (Finalizer)

 22  0xf64 0x000ef538   0x1800220 Enabled  0x00000000:0x00000000 0x000cd460     0 MTA (Threadpool Worker)

 

그리고, 대부분의 메모리 덤프에서는 다음과 같은 Finalizer Thread Callstack을 확인할 수 있다.

 

0:020> kbL

ChildEBP RetAddr  Args to Child             

023ffdf8 7c967d19 7c82202c 00000002 023ffe48 ntdll!KiFastSystemCallRet

023ffdfc 7c82202c 00000002 023ffe48 00000001 ntdll!NtWaitForMultipleObjects+0xc

023ffea4 7c822fbe 00000002 793effc8 00000000 kernel32!WaitForMultipleObjectsEx+0x11a

023ffec0 7926ed87 00000002 793effc8 00000000 kernel32!WaitForMultipleObjects+0x18

023ffee0 792034b2 000002f0 00000000 7922ec48 mscorsvr!WaitForFinalizerEvent+0x5a

023fff24 7922ec3f 00000000 809a1724 7ff97000 mscorsvr!GCHeap::FinalizerThreadStart+0x96

023fffb8 7c82482f 000d3ad8 00000000 00000000 mscorsvr!Thread::intermediateThreadProc+0x44

023fffec 00000000 7922ebfe 000d3ad8 00000000 kernel32!BaseThreadStart+0x34

 

일반적으로 Finalizer Thread를 쳐다볼 일은 없을 것이다. 하지만, Finalizer ThreadBlocking으로 인해서 예상치 못한 메모리 누수현상이 발생할 수 있다. 쉽게 isolation 할 수 있는 것은
 

ü  Hang Dump를 몇 차례 수집하였을 때, !finalizequeue의 결과의 추이를 살펴보는 것일 것이다. 계속해서 Finalizequeue 오브젝트들이 쌓이고 있다면, Finalizer Thread Block을 의심할 수 있다  

ü  GC가 정상적으로 수행되지 않는 다고 생각될 경우에도 Finalizer Thread를 살펴보는 것이 좋을 것 같다. 일반적으로 메모리 누수의 원인이 되는 오브젝트들이 Reference를 갖고 있지 않는 경우라면, 다음 GC가 수행되었을 때, 메모리가 반환될 것이다. 하지만, 이러한 참조되지 않는 오브젝트가 대량으로 메모리를 차지 하고 있다면, GC의 수행이 그다지 Health한 상태가 아닐 수 있기 때문에 GC Thread와 더불어 Finalizer Thread 역시 확인 대상이 된다.

 

다음의 Thread Stack은 흥미롭다. Finalizer thread가 맞다. 하지만, EnterCriticalSection에서 Waiting을 하고 있다. 다음의 Managed Callstack 은 임의의 Finalize Method가 처리되고 있는 것이 눈에 띈다. 만일, 지속적으로 이와 같은 Callstack Stuck 되고 있다면, 이는 Finalizer Thread Block 된 상태로 보면 된다. 해당 Finalize Method와 연관된 CriticalSection Lock 또는 관련 Thread Stack Check해보는 것이 우선일 수 있다.

 

 0:002> kbL

ChildEBP RetAddr  Args to Child             

00c9f9fc 7c967d29 7c97d266 000011d8 00000000 ntdll!KiFastSystemCallRet

00c9fa00 7c97d266 000011d8 00000000 00000000 ntdll!NtWaitForSingleObject+0xc

00c9fa3c 7c97d2b1 000011d8 00000002 04b2b008 ntdll!RtlpWaitOnCriticalSection+0x1a3

00c9fa5c 608fa5af 08ddbf68 00c9fcc4 61ccc1ae ntdll!RtlEnterCriticalSection+0xa8

WARNING: Stack unwind information not available. Following frames may be wrong.

00c9fa68 61ccc1ae 006e2628 0487af7c 00000000 oracore10!sltsmna+0xf

00c9fcc4 61ccb998 08b60128 00000004 00000000 OraClient10!kpufhndl0+0x80e

00c9fcd8 61c2602c 08b60128 00000004 00c9fcf8 OraClient10!kpufhndl+0x10

00c9fce8 1000706c 08b60128 00000004 00c9fd7c OraClient10!OCIHandleFree+0x1a

00c9fd9c 7c966f69 00c9fe30 79215bdd 00c9fe20 oci!OCIHandleFree+0x18

00c9fda4 79215bdd 00c9fe20 7921d108 ffffffff ntdll!ZwDelayExecution+0xc

00c9fe30 79215ca5 0167b394 0015e1e0 0014c698 mscorwks!MethodTable::CallFinalizer+0xee

00c9fe44 791e0984 0167b394 7c822311 00000000 mscorwks!CallFinalizer+0x84

00c9fedc 791b997b 0167b394 00000000 00000000 mscorwks!FinalizeAllObjects+0xd5

00c9ff24 791c94b4 00000000 809a1724 7ffdb000 mscorwks!GCHeap::FinalizerThreadStart+0xc2

00c9ffb8 7c82482f 0015e398 00000000 00000000 mscorwks!Thread::intermediateThreadProc+0x44

00c9ffec 00000000 791c9473 0015e398 00000000 kernel32!BaseThreadStart+0x34

 

0:002> !dumpstack -EE

Thread 2

Current frame:

  ChildEBP RetAddr    Caller,Callee

00c9fd28 0393d63f (MethodDesc 0x320be10 +0xf System.Data.OracleClient.TracedNativeMethods.OCIHandleFree)

00c9fd34 0393bb47 (MethodDesc 0x320c1f8 +0xf7 System.Data.OracleClient.OciHandle.Dispose)

00c9fd7c 00879c75 (MethodDesc 0x320c188 +0x1d System.Data.OracleClient.OciHandle.Finalize)

by 강세윤 | 2009/11/02 17:07 | Windows debugging | 트랙백 | 덧글(0)
트랙백 주소 : http://byung.egloos.com/tb/5159020
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]

:         :

:

비공개 덧글

< 이전페이지 다음페이지 >