Question
How to Measure CPU and Memory Usage from Inside a C++ Process on Windows and Linux
Question
I need to determine performance metrics from inside a running application, and the code must work on both Windows and Linux.
The metrics I want to access are:
- Total virtual memory available
- Virtual memory currently used
- Virtual memory currently used by the current process
- Total RAM available
- RAM currently used
- RAM currently used by the current process
- Percentage of CPU currently used
- Percentage of CPU currently used by the current process
This seems like a standard systems-programming task, but reliable information is often scattered, incomplete, outdated, or platform-specific. What is the correct way to gather these CPU and memory statistics from within a C++ application on Windows and Linux?
Short Answer
By the end of this page, you will understand how applications can inspect system and process resource usage from inside the program itself. You will learn the difference between system-wide and process-specific metrics, how CPU usage is calculated over time, how memory figures differ between virtual memory and RAM, and how to gather this information in C++ on both Windows and Linux.
Concept
System resource monitoring from inside an application means asking the operating system for information about memory and CPU usage.
There are two big ideas here:
- System-wide metrics: how much memory or CPU the whole machine is using
- Process-specific metrics: how much memory or CPU your program is using
A beginner-friendly way to think about this is:
- RAM is physical memory currently available on the machine
- Virtual memory is the larger address space that processes can use, including swapped or mapped memory
- CPU usage is not a fixed number you can read instantly like a variable; it is usually calculated by comparing CPU time between two moments
This matters in real programming because many applications need to:
- display health or diagnostics information
- log performance data
- trigger alerts when usage is too high
- tune workloads based on current system pressure
- help developers debug memory leaks or CPU spikes
In C++, this task is platform-dependent because Windows and Linux expose this data through different APIs and files:
- Windows commonly uses Win32 APIs such as
GlobalMemoryStatusEx,GetProcessMemoryInfo, andGetSystemTimes - Linux commonly reads from
/proc, such as/proc/meminfo,/proc/stat, and/proc/self/status
The key lesson is that there is no single portable standard-library function in C++ for this. You usually write a small abstraction layer with one implementation for Windows and another for Linux.
Mental Model
Imagine your computer is an office building.
- Total RAM available is the total desk space in the building.
- RAM currently used is how many desks are occupied.
- RAM used by your process is how much desk space your team is using.
- Virtual memory is like all the rooms your team has reserved on paper, even if nobody is currently sitting in them.
- CPU usage is like how busy the workers are over a period of time.
The important part is CPU usage: you cannot walk in and ask, "How busy are you right this exact instant?" You need to observe work done over a short interval.
So memory is usually a snapshot, while CPU usage is usually a rate measured over time.
Syntax and Examples
Core idea
In C++, you usually create small functions such as:
struct MemoryStats {
std::uint64_t totalPhysical;
std::uint64_t usedPhysical;
std::uint64_t processPhysical;
std::uint64_t totalVirtual;
std::uint64_t usedVirtual;
std::uint64_t processVirtual;
};
struct CpuTimes {
std::uint64_t idle;
std::uint64_t total;
};
Then you provide platform-specific implementations.
Windows example: total and used memory
#include <windows.h>
#include <iostream>
#include <cstdint>
int main() {
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
if (GlobalMemoryStatusEx(&memInfo)) {
std::uint64_t totalPhys = memInfo.ullTotalPhys;
std::uint64_t availPhys = memInfo.ullAvailPhys;
std::uint64_t usedPhys = totalPhys - availPhys;
std::cout << "Total RAM: " << totalPhys << " bytes\n";
std::cout << "Used RAM: " << usedPhys << " bytes\n";
}
}
What this does
GlobalMemoryStatusExasks Windows for machine-wide memory information.ullTotalPhysis total physical RAM.ullAvailPhysis currently available RAM.- Used RAM is calculated as
total - available.
Linux example: total and available memory
#include <iostream>
#include <fstream>
#include <string>
#include <cstdint>
int main() {
std::ifstream file("/proc/meminfo");
std::string key;
std::uint64_t value;
std::string unit;
std::uint64_t memTotal = 0;
std::uint64_t memAvailable = 0;
while (file >> key >> value >> unit) {
if (key == "MemTotal:") memTotal = value * 1024;
if (key == "MemAvailable:") memAvailable = value * 1024;
}
std::cout << "Total RAM: " << memTotal << " bytes\n";
std::cout << "Used RAM: " << (memTotal - memAvailable) << " bytes\n";
}
What this does
- Linux exposes memory information through
/proc/meminfo. - Values are typically given in kilobytes.
MemAvailableis usually the best field for estimating usable memory.
Process memory on Linux
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream file("/proc/self/status");
std::string line;
while (std::getline(file, line)) {
if (line.rfind("VmRSS:", 0) == 0 || line.rfind("VmSize:", 0) == 0) {
std::cout << line << '\n';
}
}
}
This prints:
VmRSS: resident set size, roughly the RAM used by the processVmSize: virtual memory size of the process
Important note about CPU
CPU usage usually needs two samples.
You typically:
- read system CPU counters
- read process CPU counters
- wait a short interval
- read them again
- compute the percentage from the difference
That is why CPU examples are often slightly more complex than memory examples.
Step by Step Execution
Example: computing Linux system CPU usage from /proc/stat
Consider this simplified line from /proc/stat:
cpu 100 20 30 400 10 0 0 0
These numbers are cumulative CPU time counters since boot.
A simple parser might use:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <thread>
#include <chrono>
struct CpuSnapshot {
unsigned long long user = 0;
unsigned long long nice = 0;
unsigned long long system = 0;
unsigned long long idle = 0;
unsigned long long iowait = 0;
unsigned long long irq = 0;
unsigned long long softirq = 0;
unsigned long long steal = 0;
};
CpuSnapshot readCpu() {
std::ifstream file("/proc/stat");
std::string line;
std::getline(file, line);
std::istringstream iss(line);
std::string cpuLabel;
CpuSnapshot s;
iss >> cpuLabel >> s.user >> s.nice >> s.system >> s.idle
>> s.iowait >> s.irq >> s.softirq >> s.steal;
return s;
}
unsigned long long total(const CpuSnapshot& s) {
return s.user + s.nice + s.system + s.idle + s.iowait + s.irq + s.softirq + s.steal;
}
int main() {
CpuSnapshot a = readCpu();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
CpuSnapshot b = readCpu();
auto totalDiff = total(b) - total(a);
auto idleDiff = b.idle - a.idle;
double usage = 100.0 * (totalDiff - idleDiff) / totalDiff;
std::cout << "CPU usage: " << usage << "%\n";
}
Step-by-step
1. First snapshot
The program reads cumulative counters from /proc/stat.
Example first sample:
- total = 560
- idle = 400
2. Wait briefly
The program sleeps for 500 ms.
3. Second snapshot
Suppose the next sample is:
- total = 620
- idle = 430
4. Compute differences
totalDiff = 620 - 560 = 60idleDiff = 430 - 400 = 30
5. Compute busy time
Busy time is:
60 - 30 = 30
6. Convert to percentage
30 / 60 * 100 = 50%
So CPU usage during that half-second interval was 50%.
Why this works
The counters are cumulative since boot, not instantaneous. By subtracting two readings, you measure how much CPU time was spent doing work during the interval.
Real World Use Cases
Monitoring dashboards
Applications often expose internal metrics so operators can see:
- current memory use
- CPU pressure
- whether one process is consuming too many resources
Background services and daemons
A service may reduce work when:
- free RAM is low
- CPU usage is high
- the current process exceeds a memory budget
Debugging and profiling
Developers log process memory and CPU usage to detect:
- memory leaks
- runaway loops
- unexpected spikes after a deployment
Desktop applications
A GUI app may show a diagnostics panel with:
- system RAM usage
- app memory usage
- app CPU percentage
Batch processing tools
A data-processing program may periodically report:
- how much RAM it is using
- whether it is saturating CPU
- whether the system is under heavy load
Autoscaling and self-throttling
Server software can make decisions based on current machine state, for example:
- reduce worker count if RAM is almost full
- delay heavy tasks if CPU is already near 100%
Real Codebase Usage
In real projects, developers usually do not scatter operating-system calls all over the codebase. Instead, they wrap resource monitoring behind a small interface.
Common pattern: platform abstraction
struct SystemStats {
std::uint64_t totalRam;
std::uint64_t usedRam;
double cpuPercent;
};
struct ProcessStats {
std::uint64_t rss;
std::uint64_t virtualSize;
double cpuPercent;
};
class ResourceMonitor {
public:
SystemStats getSystemStats();
ProcessStats getProcessStats();
};
Then you implement:
resource_monitor_windows.cppresource_monitor_linux.cpp
Guard clauses and validation
Real code checks whether the OS call succeeded before using values.
if (!GlobalMemoryStatusEx(&memInfo)) {
throw std::runtime_error("Failed to read memory status");
}
Sampling for CPU
CPU percentages are usually updated by a timer thread or metrics loop.
while (running) {
sampleCpu();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
Logging and observability
Developers often transform raw numbers into metrics systems:
- logs
- Prometheus exporters
- health endpoints
- debug pages
Distinguishing RSS vs virtual size
In production systems, RSS is usually more useful for real memory pressure, while virtual size can be misleading because reserved address space does not always mean physical memory is in use.
Error handling
On Linux, /proc might not be available in unusual environments. On Windows, certain APIs can fail due to permissions or unsupported environments. Real code should fall back gracefully or return partial results.
Common Mistakes
1. Treating virtual memory as the same as RAM
These are not the same.
- Virtual memory: address space reserved or mapped by a process
- RAM: physical memory actually resident in memory
A process can have large virtual memory usage without using that much RAM.
2. Expecting CPU usage from a single read
This is one of the most common mistakes.
Broken idea:
// Incorrect concept: one snapshot is not enough for CPU percentage
double cpu = readCpuCounter();
Why it is wrong:
- CPU counters are cumulative totals
- you need two samples and a time interval
3. Using the wrong Linux memory field
Beginners often use fields that do not mean what they think.
For example:
VmSizeis virtual memory sizeVmRSSis resident memoryMemFreealone is often not the best estimate of available memoryMemAvailableis usually more useful for modern Linux systems
4. Forgetting unit conversions
Linux /proc files often use kilobytes, while Windows APIs often return bytes.
Broken example:
std::uint64_t bytes = value; // wrong if value came from /proc/meminfo in kB
Correct:
std::uint64_t bytes = value * 1024;
5. Ignoring multi-core CPU behavior
A process can use more than one core.
Depending on the platform and formula:
- 100% may mean one full core
- values may scale across all cores
- you may need to normalize by CPU count depending on your desired output
6. Assuming process memory numbers are exact
Metrics like RSS and virtual size are useful, but they are still OS-reported views of memory usage. Shared pages, memory-mapped files, and allocator behavior can make interpretation more subtle.
7. Not handling API failure
Broken example:
MEMORYSTATUSEX memInfo;
GlobalMemoryStatusEx(&memInfo); // missing initialization and no error check
Correct:
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
if (!GlobalMemoryStatusEx(&memInfo)) {
// handle error
}
Comparisons
| Concept | What it represents | Best use |
|---|---|---|
| Total RAM | Physical memory installed/usable | Machine capacity |
| Used RAM | Physical memory currently in use | System pressure |
| Process RSS | Physical memory used by your process | Real process memory footprint |
| Process virtual memory | Reserved/mapped address space | Understanding mappings or allocation patterns |
| System CPU usage | How busy the whole machine is | Load monitoring |
| Process CPU usage | How much CPU your program uses | Profiling your app |
RSS vs Virtual Memory
| Metric | Meaning | Can be misleading? |
|---|---|---|
| RSS | Resident physical memory | Less misleading for memory pressure |
| Virtual size | Total mapped/reserved address space | Yes, often much larger than real RAM use |
Windows vs Linux data sources
| Platform | Memory source | CPU source |
|---|---|---|
| Windows | GlobalMemoryStatusEx, GetProcessMemoryInfo | GetSystemTimes, GetProcessTimes |
| Linux | /proc/meminfo, /proc/self/status | /proc/stat, /proc/self/stat |
Instant value vs sampled value
| Metric | Snapshot or sample? |
|---|---|
| RAM usage | Usually snapshot |
| Virtual memory usage | Usually snapshot |
| CPU percentage | Usually sampled over time |
Cheat Sheet
Quick reference
System memory
Windows
- Use
GlobalMemoryStatusEx ullTotalPhys= total RAMullAvailPhys= available RAMullTotalPageFile= total virtual memory/page file related valueullAvailPageFile= available virtual memory/page file related value
Linux
- Read
/proc/meminfo MemTotal= total RAMMemAvailable= available RAM- Used RAM ≈
MemTotal - MemAvailable
Process memory
Windows
- Use
GetProcessMemoryInfo WorkingSetSize≈ RAM used by processPagefileUsageoften used as process virtual/private usage indicator
Linux
- Read
/proc/self/status VmRSS= RAM used by current processVmSize= virtual memory used by current process
CPU usage
Windows
- System CPU:
GetSystemTimes - Process CPU:
GetProcessTimes - Take two snapshots and compute differences
Linux
- System CPU:
/proc/stat - Process CPU:
/proc/self/stat - Take two snapshots and compute differences
Rules to remember
- Memory is usually a snapshot.
- CPU percentage needs two samples.
- Linux
/procvalues are often in kB. - RSS is usually better than virtual size for real memory footprint.
- Normalize process CPU carefully if you want per-core or total-machine percentages.
Simple formulas
used_ram = total_ram - available_ram
cpu_percent = (busy_delta / total_delta) * 100
For system CPU on Linux:
busy_delta = total_delta - idle_delta
FAQ
How do I get CPU usage of my current process in C++?
Use platform-specific APIs and compare two snapshots over time. On Windows, use GetProcessTimes. On Linux, read /proc/self/stat and compute the delta.
How do I get RAM usage of my process on Linux?
Read /proc/self/status and look for VmRSS for resident memory and VmSize for virtual memory.
Is virtual memory the same as physical memory?
No. Virtual memory is address space reserved or mapped by a process. Physical memory is actual RAM currently in use.
Why can process CPU usage be above 100%?
On multi-core systems, a process can use more than one core. Depending on how you report it, usage may exceed 100%.
What is the best memory metric for real usage?
For process memory, RSS is usually the most practical metric. For system memory, MemAvailable is often better than MemFree on Linux.
Can I do this with only the C++ standard library?
No. The C++ standard library does not provide portable APIs for system CPU and memory statistics. You need OS-specific code.
Is /proc always available on Linux?
Usually yes on standard Linux systems, but not in every environment. Some containers, restricted systems, or non-Linux Unix systems may differ.
Mini Project
Description
Build a small cross-platform C++ resource monitor that prints basic system and process memory information and demonstrates the sampling idea needed for CPU usage. This project is useful because it mirrors how real applications collect diagnostics for logs, health pages, or performance dashboards.
Goal
Create a console program that reports total RAM, used RAM, and memory used by the current process, using separate implementations for Windows and Linux.
Requirements
- Create a
MemoryStatsstruct to hold system and process memory values. - Implement one code path for Windows and one for Linux using preprocessor checks.
- On Windows, use
GlobalMemoryStatusExandGetProcessMemoryInfo. - On Linux, read
/proc/meminfoand/proc/self/status. - Print all values in bytes and also in megabytes for readability.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.