Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 7개 있습니다.)
디버깅 기술: 68. windbg 분석 사례 - 메모리 부족
; https://www.sysnet.pe.kr/2/0/1837

디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
; https://www.sysnet.pe.kr/2/0/11808

.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
; https://www.sysnet.pe.kr/2/0/11809

.NET Framework: 944. C# - 인스턴스가 살아 있어 메모리 누수가 발생하고 있는지 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12341

디버깅 기술: 171. windbg - 인스턴스가 살아 있어 메모리 누수가 발생하고 있는지 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12342

.NET Framework: 945. C# - 닷넷 응용 프로그램에서 메모리 누수가 발생할 수 있는 패턴
; https://www.sysnet.pe.kr/2/0/12343

VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
; https://www.sysnet.pe.kr/2/0/12699




Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법

비주얼 스튜디오로 디버깅을 시작하면, "Debug" / "Windows" / "Show Diagnostic Tools (Ctrl + Alt + F2)" 메뉴로 다음과 같은 "Diagnostic Tools" 창을 띄울 수 있습니다.

vs_diag_tools_1.png

위의 창에서 "Take Snapshot" 기능이 재미있는데요, 디버깅 중에 저 버튼을 누르면 그때 당시의 힙 메모리 상태를 기록해 둡니다. 누를 때마다 한 번씩 기록하는데, 다음 화면은 3번 눌렀을 때의 모습을 보여줍니다.

vs_diag_tools_2.png

화면에 보이는 것처럼, ID 값이 1번인 snapshot이 떠진 이후, 2번 snapshot을 떴을 때까지 힙에 새로 할당된 개체는 없고, 따라서 Heap Size도 변화가 없습니다. 3번까지, 그대로니까 그사이 응용 프로그램에는 거의 변화가 없었던 것입니다.




자, 그럼 다음과 같이 간단한 프로그램을 하나 만들어,

using System;

class Program
{
    static void Main(string[] args)
    {
        MyType instance = new MyType();

        Wait(1);
        MyRoot root1 = new MyRoot();
        root1.Ref = instance;

        Wait(2);
        MyRoot root2 = new MyRoot();
        root2.Ref = instance;

        Wait(3);
    }

    private static void Wait(int step)
    {
        Console.Write($"[{step}] Wait...");
        Console.ReadLine();
    }
}

class MyRoot
{
    public object Ref;
}

class MyType
{
}

Wait(n)으로 멈출 때마다 snapshot을 떠 비교를 해보겠습니다. 그럼 다음과 같은 결과를 볼 수 있는데요,

vs_diag_tools_3.png

1번과 2번의 snapshot 사이에 root1 인스턴스만 할당이 되었는데 +2라고 나오는 것은 왜일까요? 이를 알아보기 위해 #2번 snapshot의 "+2" 링크를 누르면 다음과 같은 상세 창이 뜹니다.

vs_diag_tools_4.png

화면에 "MyRoot" 하나만 "Count Diff." 칼럼에 "+1"이 나옵니다. 그렇다면 다른 한 개는 뭘까요? 이를 확인하려면 위의 화면에서 우측 상단에 있는 필터 버튼을 눌러 (기본값으로 선택돼 있는) "Just My Code" 옵션을 해제해야 합니다.

vs_diag_tools_5.png

그럼, 상세 화면은 이제 다음과 같이 바뀝니다.

vs_diag_tools_6.png

아하, 그러니까 내 코드가 아닌 BCL 내부에서의 코드로 인해 할당된 String 개체가 하나 더 있었던 것입니다. 실제로, 예제 코드에서 Wait을 다음과 같은 식으로 미리 호출해 주면,

static void Main(string[] args)
{
    Wait(0);
    MyType instance = new MyType();

    Wait(1);
    MyRoot root1 = new MyRoot();
    root1.Ref = instance;

    Wait(2);
    MyRoot root2 = new MyRoot();
    root2.Ref = instance;

    Wait(3);
}

이전의 snapshot과는 달리 +1만 나오는 것을 확인할 수 있습니다.




우리가 관심 있고, 제어할 수 있는 것에 집중하기 위해 다시 "Just My Code" 필터링을 켜고, 보이는 "MyRoot" 인스턴스를 마우스로 선택하면 하단에 다음과 같이 "Paths to Root"가 선택된 패널이 보이며,

vs_diag_tools_7.png

해당 개체를 참조한 목록을 보여줍니다. 위에서는 "[Local Variable]"이라고 나오는데, 이는 코드에서 확인할 수 있듯이,

Wait(1);
MyRoot root1 = new MyRoot();
root1.Ref = instance;

root1 로컬 변수가 참조를 하고 있다는 것을 의미합니다. 자, 그렇다면 root1에서 "MyType instance" 변수의 참조를 Ref 변수에 할당했으니, instance의 "Paths to Root" 상황은 어떨까요? 아래는 확인한 결과입니다.

vs_diag_tools_8.png
(참고로, 목록에 개체가 많으므로 위의 화면처럼 우측 상단에서 "MyType"을 필터로 주면 쉽게 개체를 찾을 수 있습니다.)

이상하군요, 분명히 MyType 개체는 로컬 변수 "instance"뿐만 아니라 root1에서도 참조하고 있기 때문에 "Reference Count"가 2라고 나와야 하는데, 여전히 1입니다. 어허~~~ 갑자기 신뢰감이 확 떨어지는군요. ^^;

반면, 해당 개체가 참고하고 있는 "Referenced Types"를 MyRoot의 개체로 보면 어떨까요?

vs_diag_tools_9.png

오호... 참조하는 입장에서는 정확한 결과가 나옵니다. 일단, 저는 여기까지만 확인할 수 있었습니다. 혹시 저 결과에 대해 "Paths to Root"에 대한 숨겨진 의미를 알고 계신 분은 덧글 부탁드립니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 1/13/2022]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13893정성태2/27/20252225Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
13892정성태2/24/20252981닷넷: 2325. C# - PowerShell과 연동하는 방법파일 다운로드1
13891정성태2/23/20252500닷넷: 2324. C# - 프로세스의 성능 카운터용 인스턴스 이름을 구하는 방법파일 다운로드1
13890정성태2/21/20252320닷넷: 2323. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(Win32 API)파일 다운로드1
13889정성태2/20/20253050닷넷: 2322. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(성능 카운터, WMI) [1]파일 다운로드1
13888정성태2/17/20252498닷넷: 2321. Blazor에서 발생할 수 있는 async void 메서드의 부작용
13887정성태2/17/20253070닷넷: 2320. Blazor의 razor 페이지에서 code-behind 파일로 코드를 분리 및 DI 사용법
13886정성태2/15/20252572VS.NET IDE: 196. Visual Studio - Code-behind처럼 cs 파일을 그룹핑하는 방법
13885정성태2/14/20253235닷넷: 2319. ASP.NET Core Web API / Razor 페이지에서 발생할 수 있는 async void 메서드의 부작용
13884정성태2/13/20253521닷넷: 2318. C# - (async Task가 아닌) async void 사용 시의 부작용파일 다운로드1
13883정성태2/12/20253261닷넷: 2317. C# - Memory Mapped I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13882정성태2/10/20252577스크립트: 70. 파이썬 - oracledb 패키지 연동 시 Thin / Thick 모드
13881정성태2/7/20252832닷넷: 2316. C# - Port I/O를 이용한 PCI Configuration Space 정보 열람파일 다운로드1
13880정성태2/5/20253167오류 유형: 947. sshd - Failed to start OpenSSH server daemon.
13879정성태2/5/20253406오류 유형: 946. Ubuntu - N: Updating from such a repository can't be done securely, and is therefore disabled by default.
13878정성태2/3/20253197오류 유형: 945. Windows - 최대 절전 모드 시 DRIVER_POWER_STATE_FAILURE 발생 (pacer.sys)
13877정성태1/25/20253249닷넷: 2315. C# - PCI 장치 열거 (레지스트리, SetupAPI)파일 다운로드1
13876정성태1/25/20253706닷넷: 2314. C# - ProcessStartInfo 타입의 Arguments와 ArgumentList파일 다운로드1
13875정성태1/24/20253133스크립트: 69. 파이썬 - multiprocessing 패키지의 spawn 모드로 동작하는 uvicorn의 workers
13874정성태1/24/20253555스크립트: 68. 파이썬 - multiprocessing Pool의 기본 프로세스 시작 모드(spawn, fork)
13873정성태1/23/20252983디버깅 기술: 217. WinDbg - PCI 장치 열거파일 다운로드1
13872정성태1/23/20252883오류 유형: 944. WinDbg - 원격 커널 디버깅이 연결은 되지만 Break (Ctrl + Break) 키를 눌러도 멈추지 않는 현상
13871정성태1/22/20253292Windows: 278. Windows - 윈도우를 다른 모니터 화면으로 이동시키는 단축키 (Window + Shift + 화살표)
13870정성태1/18/20253731개발 환경 구성: 741. WinDbg - 네트워크 커널 디버깅이 가능한 NIC 카드 지원 확대
13869정성태1/18/20253456개발 환경 구성: 740. WinDbg - _NT_SYMBOL_PATH 환경 변수에 설정한 경로로 심벌 파일을 다운로드하지 않는 경우
13868정성태1/17/20253109Windows: 277. Hyper-V - Windows 11 VM의 Enhanced Session 모드로 로그인을 할 수 없는 문제
1  [2]  3  4  5  6  7  8  9  10  11  12  13  14  15  ...