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
정성태

... 16  17  18  19  20  21  22  23  24  25  26  27  [28]  29  30  ...
NoWriterDateCnt.TitleFile(s)
13237정성태1/30/202314315.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/202313070개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/202312561개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/202314978개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/202316143오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/202312056스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/202311286오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/202312029개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/202313930.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/202314472.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/202313386개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/202312332.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/202311672개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/202312169Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/202312075오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/202311945개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
13221정성태1/19/202312141Linux: 57. C# - 리눅스 프로세스 메모리 정보파일 다운로드1
13220정성태1/19/202311817오류 유형: 837. NETSDK1045 The current .NET SDK does not support targeting .NET ...
13219정성태1/18/202311431Windows: 220. 네트워크의 인터넷 접속 가능 여부에 대한 판단 기준
13218정성태1/17/202311284VS.NET IDE: 178. Visual Studio 17.5 (Preview 2) - 포트 터널링을 이용한 웹 응용 프로그램의 외부 접근 허용
13217정성태1/13/202312008디버깅 기술: 185. windbg - 64비트 운영체제에서 작업 관리자로 뜬 32비트 프로세스의 덤프를 sos로 디버깅하는 방법
13216정성태1/12/202311914디버깅 기술: 184. windbg - 32비트 프로세스의 메모리 덤프인 경우 !peb 명령어로 나타나지 않는 환경 변수
13215정성태1/11/202314945Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법 [1]
13214정성태1/10/202313853.NET Framework: 2087. .NET 6부터 SourceGenerator와 통합된 System.Text.Json [1]파일 다운로드1
13213정성태1/9/202312203오류 유형: 836. docker 이미지 빌드 시 "RUN apt install ..." 명령어가 실패하는 이유
13212정성태1/8/202313573기타: 85. 단정도/배정도 부동 소수점의 정밀도(Precision)에 따른 형변환 손실
... 16  17  18  19  20  21  22  23  24  25  26  27  [28]  29  30  ...