Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
(연관된 글이 3개 있습니다.)
(시리즈 글이 12개 있습니다.)
Windows: 103. 작업 관리자에서의 "Commit size"가 가리키는 메모리의 의미
; https://www.sysnet.pe.kr/2/0/1850

.NET Framework: 492. .NET CLR Memory 성능 카운터의 의미
; https://www.sysnet.pe.kr/2/0/1852

.NET Framework: 626. Commit 메모리가 낮은 상황에서도 메모리 부족(Out-of-memory) 예외 발생
; https://www.sysnet.pe.kr/2/0/11110

VC++: 107. VirtualAlloc, HeapAlloc, GlobalAlloc, LocalAlloc, malloc, new의 차이점
; https://www.sysnet.pe.kr/2/0/11152

Windows: 136. Memory-mapped File은 Private Bytes 크기에 포함될까요?
; https://www.sysnet.pe.kr/2/0/11159

.NET Framework: 845. C# - 윈도우 작업 관리자와 리소스 모니터의 메모리 값을 구하는 방법
; https://www.sysnet.pe.kr/2/0/11950

Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법
; https://www.sysnet.pe.kr/2/0/13133

.NET Framework: 2073. C# - VMMap처럼 스택 메모리의 reserve/guard/commit 상태 출력
; https://www.sysnet.pe.kr/2/0/13174

.NET Framework: 2074. C# - 스택 메모리에 대한 여유 공간 확인하는 방법
; https://www.sysnet.pe.kr/2/0/13180

Linux: 57. C# - 리눅스 프로세스 메모리 정보
; https://www.sysnet.pe.kr/2/0/13221

닷넷: 2322. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(성능 카운터, WMI)
; https://www.sysnet.pe.kr/2/0/13889

닷넷: 2323. C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(Win32 API)
; https://www.sysnet.pe.kr/2/0/13890




C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(성능 카운터, WMI)

Working Set 메모리(리눅스의 경우 Resident Set Size)는 예전에도 한번 설명했습니다.

작업 관리자에서의 "Commit size"가 가리키는 메모리의 의미
; https://www.sysnet.pe.kr/2/0/1850#working_set

즉, 해당 프로세스로 인해 소비 중인 물리 메모리의 양을 의미하는데요, 그에 더해 Private Working Set(이하, PWS)이라고 하면 다른 프로세스와 공유되지 않는 "Working Set"을 의미합니다.

관련해서 아래와 같은 질문이 하나 있는데요,

PerformanceCounter가 느린 이유?
; https://forum.dotnetdev.kr/t/performancecounter/12592

그 값을 구하기 위해 PerformanceCounter를 사용했지만 너무 느리다고 합니다. 실제로 위에 실린 코드를 좀 정리해서 그대로 실행해 보면,

using System.Diagnostics;

[assembly: System.Runtime.Versioning.SupportedOSPlatform("windows")]

public static class Program
{
    static Dictionary<int, string> processId2MemoryProcessName = new Dictionary<int, string>();

    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        CheckUserMemory();
        sw.Stop();
        Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");

        Console.ReadLine();
    }

    static void CheckUserMemory()
    {
        long totalMemory = 0L;

        Process[] processes = Process.GetProcesses();
        foreach (Process p in processes)
        {
            totalMemory += CurrentMemoryUsage(p);
        }
    }
    
    static long CurrentMemoryUsage(Process proc)
    {
        long currentMemoryUsage = 0L;
        var nameToUseForMemory = GetNameToUseForMemory(proc);

        using (var procPerfCounter = new PerformanceCounter("Process", "Working Set - Private", nameToUseForMemory))
        {
            currentMemoryUsage = procPerfCounter.RawValue;
        }

        return currentMemoryUsage;
    }

    static string GetNameToUseForMemory(Process proc)
    {
        if (processId2MemoryProcessName.ContainsKey(proc.Id))
            return processId2MemoryProcessName[proc.Id];

        var nameToUseForMemory = string.Empty;
        var category = new PerformanceCounterCategory("Process");
        var instanceNames = category.GetInstanceNames().Where(x => x.Contains(proc.ProcessName));

        foreach (var instancename in instanceNames)
        {
            using (var procPerfCounter = new PerformanceCounter("Process", "ID Process", instancename, true))
            {
                if (procPerfCounter.RawValue != proc.Id) continue;

                nameToUseForMemory = instancename;
                break;
            }
        }

        if (!processId2MemoryProcessName.ContainsKey(proc.Id))
        {
            processId2MemoryProcessName.Add(proc.Id, nameToUseForMemory);
        }

        return nameToUseForMemory;
    }
}

제 컴퓨터에서 (프로세스 수는 470개 정도였는데) 처음 한 번이 262초 정도 걸리고 이후 반복 시키면 10 ~ 13초씩 걸렸습니다.

같은 이름의 프로세스가 많을수록 CurrentMemoryUsage 메서드의 실행 속도가 늘었는데요, 가령 svchost.exe에 대해서는 290 ~ 2598ms 정도로 걸리기도 하고 단일 프로세스라면 70 ~ 100ms 정도로 걸렸습니다.

결국, 이 정도 실행 속도면 도저히 쓸 수 없는 상황입니다.




실행 속도는 3개 지점에서 느려지는데요, 모두 성능 카운터를 사용하는 부분입니다. 가령 단일 프로세스만 있는 smss.exe의 경우,

1) PerformanceCounterCategory.GetInstanceNames: 25ms
2) PerformanceCounter("Process", "ID Process", instancename, true)로 찾는 코드: 25ms
3) PerformanceCounter("Process", "Working Set - Private", ...) 코드: 24ms

이런 식으로 걸렸고 svchost.exe 중의 하나는 이렇게 걸렸습니다.

1) 27ms
2) 1250ms
3) 25ms

사실 위에서 1번과 2번 단계는 성능 카운터의 "InstanceName"을 결정하기 위해 필요한 단계인데, 이 부분의 필요성은 전에 언급한 적이 있습니다.

PerformanceCounter의 InstanceName 지정 시 주의 사항
; https://www.sysnet.pe.kr/2/0/10898

그럼 저 단계를 없애면 속도가 좀 빨라질까요? 이를 위해 InstanceName을 구하지 않아도 결정할 수 있는 "Process V2" 성능 카운터를 사용할 수 있을 것입니다.

이것을 이용하면 이제 소스코드는 다음과 같이 간단해집니다.

using System.Diagnostics;

[assembly: System.Runtime.Versioning.SupportedOSPlatform("windows")]

public static class Program
{
    static void Main(string[] args)
    {
        CheckUserMemory();
        Console.ReadLine();
    }

    static void CheckUserMemory()
    {
        long totalMemory = 0L;

        Process[] processes = Process.GetProcesses();
        foreach (Process p in processes)
        {
            totalMemory += CurrentMemoryUsage(p);
        }
    }
    
    static long CurrentMemoryUsage(Process proc)
    {
        long currentMemoryUsage = 0L;
        var nameToUseForMemory = $"{proc.ProcessName}:{proc.Id}";

        try
        {
            using (var procPerfCounter = new PerformanceCounter("Process V2", "Working Set - Private", nameToUseForMemory))
            {
                currentMemoryUsage = procPerfCounter.RawValue;
            }
        } catch { }

        return currentMemoryUsage;
    }
}

"Process"와 달리 "Process V2"는 성능 카운터를 구하는 속도도 빨라졌는데요, 전에는 25ms 정도 걸리던 것이 이제는 그 부분이 11ms로 줄었습니다. 하지만, 프로세스 수는 470개 정도인데다 최초 성능 카운터를 구하는 시점에 지연되는 시간까지 더해져 여전히 총 수행 시간이 7439ms 정도까지 걸렸습니다. (두 번째부터는 5.5초 정도에 끝납니다.)




일단, 전체 프로세스를 대상으로 일일이 PWS를 구하면서 총 사용량을 구하는 것은 저렇게 오래 걸리지만, 이건 개별 프로세스에 대한 정보가 필요한 경우 그런 것이고 만약 전체 사용량만 필요하다면 좀 더 간단하게 끝낼 수 있습니다.

왜냐하면, 성능 카운터는 "_total"이라는 항목을 제공하기 때문인데요, 그래서 다음과 같이 간단하게 구할 수 있고,

using System.Diagnostics;

[assembly: System.Runtime.Versioning.SupportedOSPlatform("windows")]

public static class Program
{
    static void Main(string[] args)
    {
        long result = CurrentMemoryUsage();
        Console.WriteLine($"{result}");
        Console.ReadLine();
    }

    static long CurrentMemoryUsage()
    {
        long currentMemoryUsage = 0L;

        try
        {
            using (var procPerfCounter = new PerformanceCounter("Process V2", "Working Set - Private", "_total"))
            {
                currentMemoryUsage = procPerfCounter.RawValue;
            }

        } catch { }

        return currentMemoryUsage;
    }
}

실행 속도는 최초 한 번만 (성능 카운터 관련 초기화로 인해) 1,654ms 정도가 걸릴 뿐, 이후로는 11ms 정도만 소요되므로 충분히 현실적인 수준으로 빨라집니다.




그런데 만약 개별 프로세스에 대한 사용량을 구하고 싶다면 어떻게 해야 할까요? 일단, 위의 결과로 봤을 때 성능 카운터는 개별 프로세스당 11ms가 걸리므로 답이 아님을 알 수 있습니다.

그렇다면 이제 Win32 API로 눈을 돌려야 하는데요, 아래의 공식 문서에 보면,

Memory Performance Information
; https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa965225(v=vs.85)

"Working Set - Private"에 대해서는 PROCESS_MEMORY_COUNTERS_EX 구조체에서 값을 담고 있지 않습니다. (심지어 성능 카운터에서는 그 값을 Windows XP/2003 시절에는 아예 제공하지도 않았다고 합니다.)

(덧글 참고하세요.) 따라서 Win32 API는 해결책에서 제외해야 합니다. ^^;

혹시 그럼 다른 방법이 있을까요? 검색해 보면, PowerShell에 기록이 있습니다.

How to get memory ( private working set ) of a process in powershell?
; https://stackoverflow.com/questions/50735404/how-to-get-memory-private-working-set-of-a-process-in-powershell

관련해서 명령어를 실행하면,

// task.ps1 (C#에서 실행하고 싶다면? C# - PowerShell과 연동하는 방법)

$StopWatch = new-object system.diagnostics.stopwatch
$StopWatch.Start()

(Get-Counter "\Process(*)\Working Set - Private").CounterSamples

$StopWatch.Stop()
Write-Host $StopWatch.Elapsed // 출력 결과: 00:00:01.2386532

상당히 빠르게 결과를 얻을 수 있는데요, 속도 측정을 해보면 1238ms 내에 모든 프로세스의 PWS 값을 구할 수 있습니다. 7초 넘게 걸리던 것이 1.2초 정도로 줄어들었다면 어느 정도는 만족할 수 있는 결과입니다.

재미있는 건, 이것 역시 성능 카운터이므로 "_total"로도 구할 수 있는데요,

// PowerShell

(Get-Counter "\Process(_total)\Working Set - Private").CounterSamples

전체를 구했을 때는 1.2초 정도 걸리던 것이 "_total" 단일 값에 대해서도 1,068ms가 소요됐습니다. 아마도 이것 역시 성능 카운터의 최초 호출에 대한 초기화로 인해 1초 정도가 걸린 것이고 나머지 값은 상당히 빠른 속도로 구한 것이 아닌가 예상됩니다.




어쨌든 중요한 것은, PowerShell의 실행 속도를 통해 성능 카운터가 아닌 다른 방법으로 개별 PWS 값을 빠르게 구할 수 있는 "어떤 방법"이 있다는 것을 확인해 볼 수 있었습니다.

자, 그럼 좀 더 검색해 볼까요? ^^

Calculating Private Working set memory from WMI class methods
; https://stackoverflow.com/questions/14773457/calculating-private-working-set-memory-from-wmi-class-methods

Win32_PerfRawData_PerfProc_Process class (Article 02/20/2014)
; https://learn.microsoft.com/en-us/previous-versions//aa394323(v=vs.85)

아하~~~ WMI로도 메모리 관련 정보를 구할 수 있다고 하는데요, Win32_PerfRawData_PerfProc_Process 클래스에 "Working Set - Private" 값이 있다고 합니다.

// wbemtest.exe로 구한 MOF 정보

[dynamic: ToInstance, provider("WmiPerfInst"), GenericPerfCtr: ToInstance, HiPerf: ToInstance, Locale(1033): ToInstance, RegistryKey("PerfProc"): ToInstance, DisplayName("Process"): ToInstance, DisplayName009("Process"): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DetailLevel(100): ToInstance]
class Win32_PerfRawData_PerfProc_Process : Win32_PerfRawData
{
	[key] string Name;
	[DisplayName("% Privileged Time"): ToInstance, DisplayName009("% Privileged Time"): ToInstance, CounterType(542180608): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 PercentPrivilegedTime;
	[DisplayName("% Processor Time"): ToInstance, DisplayName009("% Processor Time"): ToInstance, CounterType(542180608): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance, PerfDefault: ToInstance] uint64 PercentProcessorTime;
	[DisplayName("% User Time"): ToInstance, DisplayName009("% User Time"): ToInstance, CounterType(542180608): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 PercentUserTime;
	[DisplayName("Creating Process ID"): ToInstance, DisplayName009("Creating Process ID"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-1): ToInstance, DetailLevel(100): ToInstance] uint32 CreatingProcessID;
	[DisplayName("Elapsed Time"): ToInstance, DisplayName009("Elapsed Time"): ToInstance, CounterType(807666944): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-4): ToInstance, DetailLevel(100): ToInstance] uint64 ElapsedTime;
	[DisplayName("Handle Count"): ToInstance, DisplayName009("Handle Count"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint32 HandleCount;
	[DisplayName("ID Process"): ToInstance, DisplayName009("ID Process"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-1): ToInstance, DetailLevel(100): ToInstance] uint32 IDProcess;
	[DisplayName("IO Data Bytes/sec"): ToInstance, DisplayName009("IO Data Bytes/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IODataBytesPersec;
	[DisplayName("IO Data Operations/sec"): ToInstance, DisplayName009("IO Data Operations/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IODataOperationsPersec;
	[DisplayName("IO Other Bytes/sec"): ToInstance, DisplayName009("IO Other Bytes/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOOtherBytesPersec;
	[DisplayName("IO Other Operations/sec"): ToInstance, DisplayName009("IO Other Operations/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOOtherOperationsPersec;
	[DisplayName("IO Read Bytes/sec"): ToInstance, DisplayName009("IO Read Bytes/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOReadBytesPersec;
	[DisplayName("IO Read Operations/sec"): ToInstance, DisplayName009("IO Read Operations/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOReadOperationsPersec;
	[DisplayName("IO Write Bytes/sec"): ToInstance, DisplayName009("IO Write Bytes/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOWriteBytesPersec;
	[DisplayName("IO Write Operations/sec"): ToInstance, DisplayName009("IO Write Operations/sec"): ToInstance, CounterType(272696576): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint64 IOWriteOperationsPersec;
	[DisplayName("Page Faults/sec"): ToInstance, DisplayName009("Page Faults/sec"): ToInstance, CounterType(272696320): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-1): ToInstance, DetailLevel(100): ToInstance] uint32 PageFaultsPersec;
	[DisplayName("Page File Bytes"): ToInstance, DisplayName009("Page File Bytes"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-6): ToInstance, DetailLevel(100): ToInstance] uint64 PageFileBytes;
	[DisplayName("Page File Bytes Peak"): ToInstance, DisplayName009("Page File Bytes Peak"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-6): ToInstance, DetailLevel(100): ToInstance] uint64 PageFileBytesPeak;
	[DisplayName("Pool Nonpaged Bytes"): ToInstance, DisplayName009("Pool Nonpaged Bytes"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint32 PoolNonpagedBytes;
	[DisplayName("Pool Paged Bytes"): ToInstance, DisplayName009("Pool Paged Bytes"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint32 PoolPagedBytes;
	[DisplayName("Priority Base"): ToInstance, DisplayName009("Priority Base"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint32 PriorityBase;
	[DisplayName("Private Bytes"): ToInstance, DisplayName009("Private Bytes"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint64 PrivateBytes;
	[DisplayName("Thread Count"): ToInstance, DisplayName009("Thread Count"): ToInstance, CounterType(65536): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(0): ToInstance, DetailLevel(100): ToInstance] uint32 ThreadCount;
	[DisplayName("Virtual Bytes"): ToInstance, DisplayName009("Virtual Bytes"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-6): ToInstance, DetailLevel(100): ToInstance] uint64 VirtualBytes;
	[DisplayName("Virtual Bytes Peak"): ToInstance, DisplayName009("Virtual Bytes Peak"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-6): ToInstance, DetailLevel(100): ToInstance] uint64 VirtualBytesPeak;
	[DisplayName("Working Set"): ToInstance, DisplayName009("Working Set"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint64 WorkingSet;
	[DisplayName("Working Set - Private"): ToInstance, DisplayName009("Working Set - Private"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint64 WorkingSetPrivate;
	[DisplayName("Working Set Peak"): ToInstance, DisplayName009("Working Set Peak"): ToInstance, CounterType(65792): ToInstance, PerfIndex(0): ToInstance, HelpIndex(0): ToInstance, DefaultScale(-5): ToInstance, DetailLevel(100): ToInstance] uint64 WorkingSetPeak;
};

이를 기반으로 다음과 같이 C# 코드를 작성하고,

using System.Diagnostics;
using System.Management;

[assembly: System.Runtime.Versioning.SupportedOSPlatform("windows")]

namespace ConsoleApp2;

internal class Program
{
    static void Main(string[] args)
    {
        while (true)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            Console.Write($"{GetPrivateWorkingSet()}: ");
            sw.Stop();
            Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
        }
    }

    private static ulong GetPrivateWorkingSet()
    {
        ulong totalSize = 0;

        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PerfRawData_PerfProc_Process"))
        using (var data = searcher.Get())
        using (var e = data.GetEnumerator())
        {
            while (e.MoveNext())
            {
                var mo = e.Current;
                totalSize += (ulong)mo.Properties["WorkingSetPrivate"].Value;
            }
        }

        return totalSize;
    }
}

/*
// wmic로 명령행에서 실습
c:\temp> wmic /NAMESPACE:\\root\CIMV2 path Win32_PerfRawData_PerfProc_Process get WorkingSetPrivate

// WMI 구조체 검색
Get-WmiObject -Query "SELECT * FROM meta_class WHERE __class = 'Win32_PerfRawData_PerfProc_Process'" 
*/

실행해 보면, 전체 프로세스를 열거하는데 120 ~ 180ms 정도 걸리는데요, 확실히 성능 카운터를 사용하는 것보다는 빨라졌고, 게다가 이번에는 최초 호출 시의 지연 시간도 발생하지 않았습니다.

뭐 이 정도면, 그런대로 만족할 만한 수준이 되었으니 마무리하겠습니다. ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/24/2025]

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

비밀번호

댓글 작성자
 



2025-02-21 12시16분
참고로, Windows 10부터 _PROCESS_MEMORY_COUNTERS_EX2 구조체를 제공하는데 여기에는 PrivateWorkingSetSize가 있습니다. 따라서 운영체제 조건만 만족한다면 Win32 API로 구하는 것이 가장 간편한 방법입니다.

PROCESS_MEMORY_COUNTERS_EX2 structure (psapi.h)
; https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters_ex2

---------------------------------------

C# - 프로세스 메모리 중 Private Working Set 크기를 구하는 방법(Win32 API)
; https://www.sysnet.pe.kr/2/0/13890
정성태

... 76  [77]  78  79  80  81  82  83  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
12012정성태8/28/201926768.NET Framework: 859. C# - HttpListener를 이용한 HTTPS 통신 방법
12011정성태8/27/201926311사물인터넷: 57. C# - Rapsberry Pi Zero W와 PC 간 Bluetooth 통신 예제 코드파일 다운로드1
12010정성태8/27/201919248VS.NET IDE: 138. VSIX - DTE.ItemOperations.NewFile 메서드에서 템플릿 이름을 다국어로 설정하는 방법
12009정성태8/26/201920065.NET Framework: 858. C#/Windows - Clipboard(Ctrl+C, Ctrl+V)가 동작하지 않는다면?파일 다운로드1
12008정성태8/26/201919767.NET Framework: 857. UWP 앱에서 SQL Server 데이터베이스 연결 방법
12007정성태8/24/201918380.NET Framework: 856. .NET Framework 버전을 올렸을 때 오류가 발생할 수 있는 상황
12006정성태8/23/201921821디버깅 기술: 129. guidgen - Encountered an improper argument. 오류 해결 방법 (및 windbg 분석) [1]
12005정성태8/13/201919407.NET Framework: 855. 닷넷 (및 VM 계열 언어) 코드의 성능 측정 시 주의할 점 [2]파일 다운로드1
12004정성태8/12/201927699.NET Framework: 854. C# - 32feet.NET을 이용한 PC 간 Bluetooth 통신 예제 코드 [14]
12003정성태8/12/201919852오류 유형: 564. Visual C++ 컴파일 오류 - fatal error C1090: PDB API call failed, error code '3'
12002정성태8/12/201919203.NET Framework: 853. Excel Sheet를 WinForm에서 사용하는 방법 - 두 번째 이야기 [5]
12001정성태8/10/201924434.NET Framework: 852. WPF/WinForm에서 UWP의 기능을 이용해 Bluetooth 기기와 Pairing하는 방법 [1]
12000정성태8/9/201923838.NET Framework: 851. WinForm/WPF에서 Console 창을 띄워 출력하는 방법파일 다운로드1
11999정성태8/1/201918017오류 유형: 563. C# - .NET Core 2.0 이하의 Unix Domain Socket 사용 시 System.IndexOutOfRangeException 오류
11998정성태7/30/201920216오류 유형: 562. .NET Remoting에서 서비스 호출 시 SYN_SENT로 남는 현상파일 다운로드1
11997정성태7/30/201920459.NET Framework: 850. C# - Excel(을 비롯해 Office 제품군) COM 객체를 제어 후 Excel.exe 프로세스가 남아 있는 문제 [2]파일 다운로드1
11996정성태7/25/201923442.NET Framework: 849. C# - Socket의 TIME_WAIT 상태를 없애는 방법파일 다운로드1
11995정성태7/23/201927255.NET Framework: 848. C# - smtp.daum.net 서비스(Implicit SSL)를 이용해 메일 보내는 방법 [2]
11994정성태7/22/201921856개발 환경 구성: 454. Azure 가상 머신(VM)에서 SMTP 메일 전송하는 방법파일 다운로드1
11993정성태7/22/201916552오류 유형: 561. Dism.exe 수행 시 "Error: 2 - The system cannot find the file specified." 오류 발생
11992정성태7/22/201918711오류 유형: 560. 서비스 관리자 실행 시 "Windows was unable to open service control manager database on [...]. Error 5: Access is denied." 오류 발생
11991정성태7/18/201915746디버깅 기술: 128. windbg - x64 환경에서 닷넷 예외가 발생한 경우 인자를 확인할 수 없었던 사례
11990정성태7/18/201917995오류 유형: 559. Settings / Update & Security 화면 진입 시 프로그램 종료
11989정성태7/18/201916844Windows: 162. Windows Server 2019 빌드 17763부터 Alt + F4 입력시 곧바로 로그아웃하는 현상
11988정성태7/18/201919334개발 환경 구성: 453. 마이크로소프트가 지정한 모든 Root 인증서를 설치하는 방법
11987정성태7/17/201925326오류 유형: 558. 윈도우 - KMODE_EXCEPTION_NOT_HANDLED 블루스크린(BSOD) 문제 [1]
... 76  [77]  78  79  80  81  82  83  84  85  86  87  88  89  90  ...