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)
11937정성태6/11/201925545개발 환경 구성: 442. .NET Core 3.0 preview 5를 이용해 Windows Forms/WPF 응용 프로그램 개발 [1]
11936정성태6/10/201918481Math: 58. C# - 최소 자승법의 1차, 2차 수렴 그래프 변화 확인 [2]파일 다운로드1
11935정성태6/9/201920074.NET Framework: 843. C# - PLplot 출력을 파일이 아닌 Window 화면으로 변경
11934정성태6/7/201921417VC++: 133. typedef struct와 타입 전방 선언으로 인한 C2371 오류파일 다운로드1
11933정성태6/7/201919721VC++: 132. enum 정의를 C++11의 enum class로 바꿀 때 유의할 사항파일 다운로드1
11932정성태6/7/201918907오류 유형: 544. C++ - fatal error C1017: invalid integer constant expression파일 다운로드1
11931정성태6/6/201919414개발 환경 구성: 441. C# - CairoSharp/GtkSharp 사용을 위한 프로젝트 구성 방법
11930정성태6/5/201919950.NET Framework: 842. .NET Reflection을 대체할 System.Reflection.Metadata 소개 [1]
11929정성태6/5/201919500.NET Framework: 841. Windows Forms/C# - 클립보드에 RTF 텍스트를 복사 및 확인하는 방법 [1]
11928정성태6/5/201918360오류 유형: 543. PowerShell 확장 설치 시 "Catalog file '[...].cat' is not found in the contents of the module" 오류 발생
11927정성태6/5/201919574스크립트: 15. PowerShell ISE의 스크립트를 복사 후 PPT/Word에 붙여 넣으면 한글이 깨지는 문제 [1]
11926정성태6/4/201919996오류 유형: 542. Visual Studio - pointer to incomplete class type is not allowed
11925정성태6/4/201919898VC++: 131. Visual C++ - uuid 확장 속성과 __uuidof 확장 연산자파일 다운로드1
11924정성태5/30/201921565Math: 57. C# - 해석학적 방법을 이용한 최소 자승법 [1]파일 다운로드1
11923정성태5/30/201921136Math: 56. C# - 그래프 그리기로 알아보는 경사 하강법의 최소/최댓값 구하기파일 다운로드1
11922정성태5/29/201918594.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/201924502Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/201916140.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/201920470Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/25/201921252Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/201922232Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
11916정성태5/24/201920061Math: 51. MathNET + OxyPlot을 이용한 간단한 통계 정보 처리 - Histogram파일 다운로드1
11915정성태5/24/201923184Linux: 11. 리눅스의 환경 변수 관련 함수 정리 - putenv, setenv, unsetenv
11914정성태5/24/201922186Linux: 10. 윈도우의 GetTickCount와 리눅스의 clock_gettime파일 다운로드1
11913정성태5/23/201918853.NET Framework: 838. C# - 숫자형 타입의 bit(2진) 문자열, 16진수 문자열 구하는 방법파일 다운로드1
11912정성태5/23/201918824VS.NET IDE: 137. Visual Studio 2019 버전 16.1부터 리눅스 C/C++ 프로젝트에 추가된 WSL 지원
... 76  77  78  79  [80]  81  82  83  84  85  86  87  88  89  90  ...