Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

(시리즈 글이 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# - 윈도우 작업 관리자와 리소스 모니터의 메모리 값을 구하는 방법

다음의 그림을 보고 시작할까요? ^^

windows_memory_1.png

위의 수치를 보면서 작업 관리자와 리소스 모니터의 용어를 정리해 보겠습니다.

작업 관리자의 "MEMORY" 32GB == 리소스 모니터의 "Installed" 32,768MB
작업 관리자의 "Memory usage" 31.9GB == 리소스 모니터의 "Total" 32,683MB

작업 관리자의 "In use" 27.0GB == 리소스 모니터의 "In Use" 27,604MB
작업 관리자의 "Available" 4.9GB == 리소스 모니터의 "Available" == "Standby + Free" 4,977MB

작업 관리자의 "Cached" 4.9GB == 리소스 모니터의 "Cached" == "Modified + Standby" 4,938MB
작업 관리자의 "Hardware reserved" 84.5MB == 리소스 모니터의 "Hardware Reserved" 85MB

좀 더 자세한 설명은, 리소스 모니터의 용어에 마우스를 올려 보면 나오는 다음의 툴팁을 참고할 수 있습니다.

  • Hardware Reserved: Memory that is reserved for use by the BIOS and some drivers for other peripherals
  • In Use: Memory used by processes, drivers, or the operating system
  • Modified: Memory whoose contents must be written to disk before it can be used for another purpose
  • Standby: Memory that contains cached data and code that is not actively in use
  • Free: Memory that does not contain any valuable data, and that will be used first when processes, drivers or the operating system need more memory

  • Available: Amount of memory (including Standby and Free memory) that is immediately available for use by processes, drivers, or the operating system
  • Cached: Amount of memory (including Standby and Modified memory) containing cached data and code for rapid access by processes, drivers, or the operating system
  • Total: Amount of physical memory available to the operating system, device drivers, and processes
  • Installed: Amount of physical memory installed in the computer

자, 그럼 이제 쉽게 정리해 볼까요? ^^

우선, 여러분이 구매한 PC의 물리 메모리로 장착한 용량을 작업 관리자의 "Memory" 또는 리소스 모니터의 "Installed"를 통해서 알 수 있습니다. 하지만 대개의 경우 그 물리 메모리를 전부 쓸 수 있는 것은 아니고 컴퓨터의 장치 요소들 중에는 메모리를 요구하는 것이 있어 그런 것들에 할당되는 메모리는 사실상 응용 프로그램에서 사용할 수 없으므로 제외하게 됩니다. 그 메모리가 바로 작업 관리자와 리소스 모니터의 "Hardware reserved"입니다.

따라서 제 컴퓨터는 총 32,768MB의 RAM이 설치되어 있는데 그중 85MB를 제외한 32,683MB의 메모리만 쓸 수 있는 것입니다.




이제 32,683MB 메모리에서, 실질적으로 운영체제 및 프로그램이 사용하는 메모리를 빼야 합니다. 그것이 바로 "In Use"로 말 그대로 "사용 중"인 메모리입니다.

"사용 중"이긴 하지만, 동일한 프로그램을 올려 놓은 상황에서 만약 메모리가 (32GB가 아닌) 16GB라면 "In Use" 메모리가 줄어들 수도 있습니다. 왜냐하면 저것은 프로그램의 "Working Set"이기 때문에 메모리가 충분하면 더 올려놓고, 부족하면 시기적절하게 페이징 해버리기 때문입니다. 따라서 동일한 프로그램인데도 컴퓨터의 사양에 따라 다르게 나올 수 있으므로 특별한 상황이 아니라면 그렇게 다르다는 사실에 크게 주목하지 않아도 됩니다.

"In Use"를 제외하면 이제 Modified, Standby, Free 영역이 남습니다.

이 중에서 Modified와 Standby는 시스템의 성능을 높이기 위한 목적의 Cache 용도로 운영체제가 일부러, 나쁘게 말하면 ^^ 요행을 바라며 쓸데없이 사용 중인 메모리입니다. 하지만 Standby가 읽기 용도로 올려진 cache인 반면, 그중에서 운이 좋아 "쓰기"까지 진행된 영역이 "Modified"입니다. 이 2가지는 같은 cache 용도였지만 해당 메모리를 해제하는 시점의 처리 방식이 달라진다는 차이점이 있습니다. 즉, Standby는 별다른 조치 없이 바로 해제할 수 있지만 Modified는 그전에 반드시 디스크에 기록해야 하므로 부하가 발생하는 것입니다.

따라서, 운영체제는 특정 프로그램이 메모리를 요구했을 때 "Standby" 영역을 아무런 부하 없이 응용 프로그램에게 할당할 수 있기 때문에 "Standby"와 "Free" 영역을 합쳐 가용(Available) 메모리라고 하는 것입니다. 결국 리소스 모니터에서 "Free"가 39MB만 남았다고 해서 자신의 시스템이 현재 심각한 RAM 부족 현상이 발생하는 것은 아니라는 것을 의미합니다.




자, 그럼 "리소스 모니터"가 보여주는 메모리를 Win32 API를 이용해 프로그램으로 구해볼까요? ^^ 우선 시스템에 설치된 RAM 용량을 GetPhysicallyInstalledSystemMemory API로 가져올 수 있습니다.

GetPhysicallyInstalledSystemMemory function
; https://learn.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getphysicallyinstalledsystemmemory

public class SafeNativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetPhysicallyInstalledSystemMemory(out ulong MemoryInKilobytes);
}

SafeNativeMethods.GetPhysicallyInstalledSystemMemory(out ulong physicalMemory);

그다음 "Hardware Reserved" 용량은 어떻게 구해야 할까요? 이 값은 리소스 모니터의 "Available" 메모리를 가져와 위에서 구한 physicalMemory에서 빼면 구할 수 있습니다. 이를 위해 GetPerformanceInfo API로부터,

GetPerformanceInfo function
; https://learn.microsoft.com/en-us/windows/desktop/api/psapi/nf-psapi-getperformanceinfo

Total 메모리(즉, Available)를 구하고,

[DllImport("psapi.dll", SetLastError = true)]
public static extern bool GetPerformanceInfo(out PERFORMANCE_INFORMATION pPerformanceInformation, uint cb);

PERFORMANCE_INFORMATION pi = new PERFORMANCE_INFORMATION();
pi.cb = (uint)Marshal.SizeOf(pi);
SafeNativeMethods.GetPerformanceInfo(out pi, pi.cb);

ulong pageSize = pi.PageSize.ToUInt64();
ulong physicalTotal = pi.PhysicalTotal.ToUInt64(); // == Available 메모리 용량

Console.WriteLine($"PhysicalTotal == \t{physicalTotal * pageSize / 1024 / 1024} MB");

이 값을 통해 "Hardware Reserved" 크기를 유추할 수 있습니다.

double reserved = (physicalMemory - (pi.Total / 1024.0));
long reservedMB = (long)Math.Round(reserved / 1024.0);
Console.WriteLine($"Reserved == \t\t\t{reservedMB} MB");




가용 메모리(Standby + Free)를 구할 수 있는 또 다른 API가 있습니다.

GlobalMemoryStatusEx function
; https://learn.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex

MEMORYSTATUSEX structure
; https://learn.microsoft.com/en-us/windows/desktop/api/sysinfoapi/ns-sysinfoapi-_memorystatusex

ullAvailPhys: The amount of physical memory currently available, in bytes. This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. It is the sum of the size of the standby, free, and zero lists.


[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX buffer);

MEMORYSTATUSEX globalMemoryStatus = new MEMORYSTATUSEX();
SafeNativeMethods.GlobalMemoryStatusEx(ref globalMemoryStatus);

Console.WriteLine($"MEMORYSTATUSEX.ullAvailPhys: {globalMemoryStatus.ullAvailPhys / 1024.0 / 1024.0} MB");

재미있는 것은 가용 메모리는 구해도 Standby와 Free를 개별적으로 구할 수는 없다는 점입니다. (아래에서 성능 카운터를 이용해 구하는 방법이 나옵니다.)

Standby의 경우 GetPerformanceInfo의 PERFORMANCE_INFORMATION.SystemCache로 근사치를 구할 수 있는데, 애석하게도 이 값은 리소스 모니터에서 나온 "Standby"에 비해서는 항상 작게 나옵니다.

마지막으로 구할 수 있는 수치가 "In Use + Modified"입니다. 이것은 (Installed가 아닌) 전체 메모리에서 Available 메모리를 빼면 됩니다.

ulong inuse_modified = (ulong) ((pi.Total - pi.Available) / 1024.0);
Console.WriteLine($"Total - Available - HardwareReserved: {inuse_modified / 1024.0:#,#.0} MB ({inuse_modified / 1024.0 / 1024.0:#,#.0} GB)");

마찬가지로 (성능 카운터의 도움을 받지 않고) API 수준에서 "In Use"와 "Modified"를 개별적으로 구할 수 있는 방법은 없습니다. (혹시, 아시는 분은 덧글 부탁드립니다. ^^)




Win32 API가 아닌, Performance Counter로 눈을 돌리면 이제 "Modified" 값을 구할 수 있습니다.

\Memory\Modified Page List Bytes 

이 값을 구할 수 있으니, 위에서 구했던 "inuse_modified" 값에서 빼면 "In Use" 용량도 구할 수 있습니다. 또한 다음의 성능 카운터는,

\Memory\Free & Zero Page List Bytes

리소스 관리자의 "Free" 영역의 수치이므로 역시 이전에 구했던 가용 메모리에서 빼면 "Standby" 용량도 구할 수 있습니다. 그럼, 전부 구했군요. ^^ 다음의 소스 코드는 위에서 설명한 내용을 바탕으로 리소스 모니터와 작업 관리자에서 보여주는 수치를 구합니다.

using System;
using System.Diagnostics;
using System.Threading;

class Program
{
    private static PerformanceCounter freeMemory;
    private static PerformanceCounter modifiedMemory;

    static void Main(string[] args)
    {
        freeMemory = new PerformanceCounter("Memory", "Free & Zero Page List Bytes", true);
        modifiedMemory = new PerformanceCounter("Memory", "Modified Page List Bytes", true);

        while (true)
        {
            PERFORMANCE_INFORMATION pi = new PERFORMANCE_INFORMATION();
            pi.Initialize();
            SafeNativeMethods.GetPerformanceInfo(out pi, pi.cb);

            Console.WriteLine("[Resource Monitor]");

            SafeNativeMethods.GetPhysicallyInstalledSystemMemory(out ulong installedMemory);

            double reserved = (installedMemory - (pi.Total / 1024.0));
            ulong modified = (ulong)modifiedMemory.RawValue;
            ulong inuse = pi.Total - pi.Available - modified;

            long reservedMB = (long)Math.Round(reserved / 1024.0);
            Console.WriteLine($"Hardware Reserved: {reservedMB} MB");

            Console.WriteLine($"In Use: {inuse / 1024 / 1024} MB");
            Console.WriteLine($"Modified: {modified / 1024 / 1024} MB");

            ulong free = (ulong)freeMemory.RawValue;
            ulong standby = pi.Available - free;
            Console.WriteLine($"Standby: {standby / 1024 / 1024} MB");
            Console.WriteLine($"Free: {free / 1024 / 1024} MB");
            Console.WriteLine();
            Console.WriteLine($"Available: {pi.Available.MB()} MB");
            Console.WriteLine($"Cached: {(standby + modified).MB()} MB");
            Console.WriteLine($"Total: {pi.Total.MB()} MB");
            Console.WriteLine($"Installed: {installedMemory / 1024} MB");

            MEMORYSTATUSEX globalMemoryStatus = new MEMORYSTATUSEX();
            globalMemoryStatus.Initialize();
            SafeNativeMethods.GlobalMemoryStatusEx(ref globalMemoryStatus);

            Console.WriteLine();
            Console.WriteLine("[Task Manager]");

            Console.WriteLine($"Memory: {installedMemory / 1024.0 / 1024.0} GB");
            Console.WriteLine($"Memory usage: {pi.Total / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine();
            Console.WriteLine($"In use: {inuse / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine($"Available: {pi.Available / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine($"Committed: {pi.Commit / 1024.0 / 1024.0 / 1024.0:#.0} / {globalMemoryStatus.ullTotalPageFile / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine($"Cached: {(standby + modified) / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine($"Paged pool: {pi.KernelPage / 1024.0 / 1024.0 / 1024.0:#.0} GB");
            Console.WriteLine($"Non-paged pool: {pi.KernelNonPage / 1024.0 / 1024.0:#} GB");

            Console.WriteLine();
            Thread.Sleep(1000);
        }
    }
}

그리고 다음은 이렇게 구한 수치와 실제 리소스 모니터/작업 관리자의 수치를 비교하는 모습입니다.

windows_memory_2.png

서로 구하는 시간 차가 약간 있는 것을 감안하면 대충 맞는 걸로 보입니다. ^^

(이 글의 예제 프로젝트는 github에 있습니다.)




그 외에, 메모리 사용량을 알아낼 수 있는 방법들을 검색해 보면 위에서 설명한 범위 내에서 구해지는 것들입니다. 예를 들어, WMI의 FreePhysicalMemory, TotalVisibleMemorySize 등을 사용할 수 있다고 나오는데,

wmic OS get FreePhysicalMemory, TotalVisibleMemorySize

실제로 구해지는 값을 보면, FreePhysicalMemory는 Available 용량을, TotalVisibleMemorySize는 물리 메모리에서 "Hardware Reserved" 용량을 뺀 값(단위: KB)에 해당합니다. 그 외에 TotalPhysicalMemory도 있는데,

wmic computersystem get TotalPhysicalMemory

마찬가지로 TotalVisibleMemorySize 값(단위: 바이트)과 같습니다.




기타 다음의 글들도 읽어보시면 도움이 될 것입니다.

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

The usable memory may be less than the installed memory on Windows 7-based computers
; https://support.microsoft.com/en-gb/help/978610/the-usable-memory-may-be-less-than-the-installed-memory-on-windows-7-b




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







[최초 등록일: ]
[최종 수정일: 3/9/2024]

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

비밀번호

댓글 작성자
 




... 151  152  153  154  155  156  157  [158]  159  160  161  162  163  164  165  ...
NoWriterDateCnt.TitleFile(s)
1100정성태8/17/201128923.NET Framework: 236. SqlDbType - DateTime, DateTime2, DateTimeOffset의 차이점파일 다운로드1
1099정성태8/15/201128350오류 유형: 132. 어느 순간 갑자기 접속이 안 되는 TFS 서버
1098정성태8/15/201150357웹: 24. 네이버는 어떻게 로그인 처리를 할까요? [2]
1097정성태8/15/201121657.NET Framework: 235. 메서드의 메타 데이터 토큰 값으로 클래스를 찾아내는 방법
1096정성태8/15/201125797디버깅 기술: 42. Watson Bucket 정보를 이용한 CLR 응용 프로그램 예외 분석 - (2)
1095정성태8/14/201126242디버깅 기술: 41. Windbg - 비정상 종료된 닷넷 프로그램의 StackTrace에서 보이는 offset 값 의미
1094정성태8/14/201130614오류 유형: 131. Fiddler가 강제 종료된 경우, 웹 사이트 방문이 안되는 현상
1093정성태7/27/201124240오류 유형: 130. Unable to connect to the Microsoft Visual Studio Remote Debugging Monitor ... Access is denied.
1092정성태7/22/201126640Team Foundation Server: 46. 코드 이외의 파일에 대해 소스 제어에서 제외시키는 방법
1091정성태7/21/201125680개발 환경 구성: 128. WP7 Emulator 실행 시 audiodg.exe의 CPU 소모율 증가 [2]
1089정성태7/18/201131258.NET Framework: 234. 왜? Button 컨트롤에는 MouseDown/MouseUp 이벤트가 발생하지 않을까요?파일 다운로드1
1088정성태7/16/201124332.NET Framework: 233. Entity Framework 4.1 - 윈도우 폰 7에서의 CodeFirst 순환 참조 문제파일 다운로드1
1087정성태7/15/201127024.NET Framework: 232. Entity Framework 4.1 - CodeFirst 개체의 직렬화 시 순환 참조 해결하는 방법 - 두 번째 이야기파일 다운로드1
1086정성태7/14/201128453.NET Framework: 231. Entity Framework 4.1 - CodeFirst 개체의 직렬화 시 순환 참조 해결하는 방법 [1]파일 다운로드1
1085정성태7/14/201128894.NET Framework: 230. Entity Framework 4.1 - Code First + WCF 서비스 시 EndpointNotFoundException 오류 - 두 번째 이야기파일 다운로드1
1084정성태7/11/201134177.NET Framework: 229. SQL 서버 - DB 테이블의 데이터 변경에 대한 알림 처리 [4]파일 다운로드1
1083정성태7/11/201128222.NET Framework: 228. Entity Framework 4.1 - Code First + WCF 서비스 시 EndpointNotFoundException 오류
1082정성태7/10/201127799.NET Framework: 227. basicHttpBinding + 사용자 정의 인증 구현 [2]파일 다운로드1
1081정성태7/9/201127114VC++: 53. Windows 7에서 gcc.exe 실행 시 Access denied 오류 [2]
1080정성태7/8/201125609웹: 23. Sysnet 웹 사이트의 HTML5 변환 기록 - 두 번째 이야기파일 다운로드1
1079정성태7/6/201130027오류 유형: 129. Hyper-V + Realtek 랜카드가 설치된 시스템의 BSOD 현상 [2]
1078정성태7/5/201137560VC++: 52. Chromium 컴파일하는 방법 [2]
1077정성태6/24/201135185.NET Framework: 226. HttpWebRequest 타입의 HaveResponse 속성 이야기파일 다운로드1
1076정성태6/23/201129322오류 유형: 128. SQL Express - User Instance 옵션을 사용한 경우 발생하는 오류 메시지 유형 2가지
1075정성태6/21/201124945VS.NET IDE: 69. 윈폰 프로젝트에서 WCF 서비스 참조할 때 Reference.cs 파일이 비어있는 경우
1074정성태6/20/201125030.NET Framework: 225. 닷넷 네트워크 라이브러리의 트레이스 기능파일 다운로드1
... 151  152  153  154  155  156  157  [158]  159  160  161  162  163  164  165  ...