7

By "accurately describes the amount of used memory", I mean does it report (or can it be calculated) the same amount of memory use as described in Activity Monitor?

Activity monitor memory use: 14.20 gb

My intent is to find a method to programmatically access the amount of used physical memory in the system. In .NET Core 3.1 on Mac, this is impossible. My next option is to query a command line tool. My concern is that the figures reported below do not total up to 14.20 GB of used memory as expected in the above screenshot. This is why I claim they are inaccurate.

Below is what I've tried so far and why this is not a duplicate question. All commands were ran a second apart from each other after the above screenshot was taken.

Wired Memory:       5247 MB
Active Memory:      4170 MB
Inactive Memory:    3813 MB
Free Memory:        609 MB
Real Mem Total (ps):    11226.922 MB
  • top's "PhysMem" field claims that I'm using 15 GB on my system is in use. This is somewhat close, but it is has very rough rounding and sometimes rounds up to 16 GB in use.
PhysMem: 15G used (5269M wired), 960M unused.
  • How to calculate used memory on Mac OS by command line? The only answer describes vm_stat which is inaccurate and does not properly answer the question. I've read from around the web that to get the amount of used memory, you must sum "Active", "Inactive", and "Wired down" rows, then multiply it by page size. On this Catalina machine, this comes up to 13.8 GB (output below). Still kind of close, but this math is off by more than a GB on another Catalina machine.
Mach Virtual Memory Statistics: (page size of 4096 bytes)
Pages free:                              161443.
Pages active:                           1065326.
Pages inactive:                          974107.
Pages speculative:                        89929.
Pages throttled:                              0.
Pages wired down:                       1343201.
Pages purgeable:                          18630.
"Translation faults":                2149730807.
Pages copy-on-write:                  123210957.
Pages zero filled:                    965651547.
Pages reactivated:                    129523087.
Pages purged:                           3285194.
File-backed pages:                       292639.
Anonymous pages:                        1836723.
Pages stored in compressor:             3394212.
Pages occupied by compressor:            559818.
Decompressions:                       166263782.
Compressions:                         206698757.
Pageins:                              358184714.
Pageouts:                              10064704.
Swapins:                              123517427.
Swapouts:                             126184348.
  • memory_pressure doesn't seem to provide any accurate information related to memory usage. "System-wide memory free percentage" claims to be 54%, but 14.2 / 16 GB are in use.
The system has 2147483648 (524288 pages with a page size of 4096).

Stats:
Pages free: 162828
Pages purgeable: 16659
Pages purged: 3285194

Swap I/O:
Swapins: 123517427
Swapouts: 126184348

Page Q counts:
Pages active: 1062737
Pages inactive: 972247
Pages speculative: 89910
Pages throttled: 0
Pages wired down: 1346397

Compressor Stats:
Pages used by compressor: 559818
Pages decompressed: 166263778
Pages compressed: 206698757

File I/O:
Pageins: 358184697
Pageouts: 10064704

System-wide memory free percentage: 54%
10
  • Can you please run all the tests on the same machine (and basically at the same time) so different figures become comparable? Right now it‘s not clear what kind of gap you see.
    – nohillside
    Commented Jul 7, 2021 at 17:10
  • 1
    @nohillside Thanks for the suggestion. I updated the original post with comparable figures. Commented Jul 7, 2021 at 17:35
  • 1
    Superb research. I’m not sure I get what you’re trying to do or how “used” isn’t accurate. The figures reported are all highly precise and accurate, so I don’t know what problem you’re trying to solve where this information isn’t helpful.
    – bmike
    Commented Jul 7, 2021 at 17:42
  • 1
    @bmike I want a way to programmatically access the amount of physical memory currently being used by the system. In .NET Core 3.1 on Mac, this isn't possible. The next solution (which is what this post concerns) is to try querying a command line tool that can do the job instead. The discrepancy between the numbers I'm seeing and what Activity Monitor reports is concerning and why I claim they are "inaccurate". I'll update my original post to make my intent more clear. Commented Jul 7, 2021 at 18:22
  • 2
    One possible source of confusion is Gigabytes (1 GB =1000^3 bytes) vs Gibibytes (1 GiB =1024^3 bytes =~ 1.074 GB). Is it possible some of your numbers are in one of these, and some are in the other? Commented Jul 7, 2021 at 19:25

1 Answer 1

15

The strict (rather trite) answer to the question is that the amount of memory being used is equal the amount physical memory. But let's be more helpful and get some answers as to how that physical memory is being used.

Just to complicate things there are at least two ways sets of numbers which add up to the physical memory:

  • "Traditional" unix style with wired, active, inactive, free, etc. with additions for compressed memory.
  • "Activity Monitor" memory which introduces App Memory.

The numbers we need can be extracted from the results of these commands:

  • vm_stat which gives us most of what we need,
  • selected numbers from sysctl, and
  • memory_pressure from which I use just one number.

There is overlap between the results of these commands and the memory_pressure rather perversely presents the inverse of Activity Monitor's pressure.

Memory Pressure is just a number which provides a simple way of indicating memory load. It can be calculated from vm_stat results or just taken from memory pressure. We should not worry too much about its physical interpretation - it just an indicative number.

I apologise upfront that I use a python script to collect the data and present it in a digestible format. First the results.

On my 8GB MacBook, right now I get this:

Traditional memory:
Wired Memory:           2.174 GB
Active Memory:          2.428 GB
Inactive Memory:        2.359 GB
Speculative:            0.067 GB
Throttled:              0.000 GB
Free Memory:            0.016 GB
Compressed:             0.937 GB
Total:                  7.981 GB

Activity Monitor memory
App Memory:             2.586 GB
Wired Memory:           2.174 GB
Compressed:             0.937 GB
Memory Used:            5.698 GB
Cached Files:           2.268 GB
Total:                  7.981 GB

Swap Used:              0.508 GB    520.000 MB
Memory Pressure:        3.1   GB     39 percent

So, two ways of presenting numbers which add up to very nearly 8GB. The numbers are all in RAM sizes which are based on powers of 2, i.e. 1KB RAM is 1024 bytes.

Plus, at the end, swap used and memory pressure.

Note that "Compressed" means the physical RAM which is used by the Compressor which has compressed a hopefully larger amount of uncompressed RAM.

Here is the (not very elegant) python script:

#!/usr/bin/python3

import sys, subprocess, re

f1 = 0.00000000093132257    # 1/(1024*1024*1024) Converts bytes to GB

# Get the pagesize since it varies between x86 and apple silicon
pagesize = int(subprocess.Popen(['pagesize'], stdout=subprocess.PIPE).communicate()[0].decode())

# Get memory info from VM_STAT andprocess into a dictionary
vm = subprocess.Popen(['vm_stat'], stdout=subprocess.PIPE).communicate()[0]
# Process vm_stat
vmLines = vm.decode().split('\n')
sep = re.compile(':[\s]+')
vmStats = {}
for row in range(1,len(vmLines)-2):
    rowElements = sep.split(vmLines[row].strip())
    vmStats[(rowElements[0])] = int(rowElements[1].strip('\.')) * pagesize

# 2 quantities from sysctl
sy = subprocess.Popen(['sysctl','vm.page_pageable_internal_count'], stdout=subprocess.PIPE).communicate()[0]
p1 = sy.decode().find(':')
page_pageable_internal_count = float(sy[p1+1:50]) * pagesize
sy = subprocess.Popen(['sysctl','vm.swapusage'], stdout=subprocess.PIPE).communicate()[0]
p1 = sy.decode().find('used')
p2 = sy.decode().find('M',p1)
swapUsed = float(sy[p1+7:p2])   # MBytes

# Pressure - just get the pressure value
sy = subprocess.Popen(['memory_pressure'], stdout=subprocess.PIPE).communicate()[0]
p1 = sy.decode().find('tage:')
p2 = sy.decode().find('%')
mp = 100 - int(sy[p1+6:p2])

# There are 2 tricks to get Activity Monitor's App Memory (which is best?)
#appMemory = page_pageable_internal_count - vmStats["Pages purgeable"] 
appMemory = vmStats["Anonymous pages"] - vmStats["Pages purgeable"] 

print('Traditional memory:')
print ('Wired Memory:\t\t%9.3f GB' % ( vmStats["Pages wired down"] * f1 ))
print ('Active Memory:\t\t%9.3f GB' % ( vmStats["Pages active"] * f1 ))
print ('Inactive Memory:\t%9.3f GB' % ( vmStats["Pages inactive"] * f1 ))
print ('Speculative:\t\t%9.3f GB' % ( vmStats["Pages speculative"] * f1 ))
print ('Throttled:\t\t%9.3f GB' % ( vmStats["Pages throttled"] * f1 ))
print ('Free Memory:\t\t%9.3f GB' % ( vmStats["Pages free"] * f1 ))
print ('Compressed:\t\t%9.3f GB' % ( vmStats["Pages occupied by compressor"] * f1 ))
# These add up close to phyical RAM
print ('Total:\t\t\t%9.3f GB' % ( (vmStats["Pages free"] + vmStats["Pages wired down"] + vmStats["Pages active"] + vmStats["Pages inactive"] + vmStats["Pages speculative"] + vmStats["Pages throttled"] + vmStats["Pages occupied by compressor"]) * f1 ))
print ('')
print ('Activity Monitor memory')
print ('App Memory:\t\t%9.3f GB' % ( appMemory * f1 ))
print ('Wired Memory:\t\t%9.3f GB' % ( vmStats["Pages wired down"] * f1 ))
print ('Compressed:\t\t%9.3f GB' % ( vmStats["Pages occupied by compressor"] * f1 ))
print ('Memory Used:\t\t%9.3f GB' % ( (appMemory + vmStats["Pages wired down"] + vmStats["Pages occupied by compressor"] ) * f1 ))
print ('Cached Files:\t\t%9.3f GB' % ( (vmStats["File-backed pages"] + vmStats["Pages purgeable"]) * f1 ))
# and these add up to physical rAM
print ('Total:\t\t\t%9.3f GB' % ( (appMemory + vmStats["Pages wired down"] + vmStats["Pages occupied by compressor"] + vmStats["File-backed pages"] + vmStats["Pages purgeable"] + vmStats["Pages free"]) * f1))
print ('')
print ('Swap Used:\t\t%9.3f GB  %9.3f MB' % ( swapUsed * 0.0009765625, swapUsed ))
print ('Memory Pressure:\t%7.1f   GB %6.0f percent' % ( (appMemory + vmStats["Pages wired down"] + vmStats["Pages occupied by compressor"] + vmStats["File-backed pages"] + vmStats["Pages purgeable"] + vmStats["Pages free"]) * f1 * mp / 100, mp) )
    
sys.exit(0);

I use a very similar script called by the snmpd daemon to provide input to Cacti. This displays activity in graphical format, like this:

Cacti

That is for the last 24 hours (my MacBook was sleeping for some of that) with the numbers being the most recent values of each parameter.

9
  • On my M2 Mac with macOS Sonoma 14.2.1 and 24 GB memory, the "Memory Used" output by this script does not match the amount shown by Activity Monitor, though. The number is about 1 GB higher in Activity Monitor. (The numbers for App Memory, Wired Memory and Compressed at the right-hand side in Activity Monitor match the script output and are thus also "missing" a gigabyte in total, compared to Memory Used.)
    – Otto G
    Commented Jan 14 at 11:56
  • 1
    @OttoG Activity Monitor has always(?) had problems with Memory Used. On Intel, 1) MemUsed is slightly more than App+Wired+Compressed even though the layout suggests they should be the same, 2) MemUsed + Cache + free is greater than Physical RAM and when free is small, MemUsed+Cache is greater than Physical. I reported this to Apple Feedback.
    – Gilby
    Commented Jan 15 at 7:59
  • 1
    @OttoG On Apple Silicon (Mx), MemUsed is in even greater problems. As well as what you have noticed, MemUsed+Cache is 16.8GB (never mind about adding in Free) on my 16GB M3 MBP! Ignoring MemUsed problem, what is new on AS is App+Wired+Comp+Cache+free is about 15.2 GB - where is the remaining 0.8 GB?? iStatMenus shows the same discrepancy. I suspect (don't know) that the apparently missing RAM is because of changes to memory architecture with AS Macs. There should be some other internal counter which shows us this (but I have no idea what).
    – Gilby
    Commented Jan 15 at 8:08
  • Thank you @Gilby! By the way, you mention that Memory Pressure could be calculated from vm_stat results. How would that be done? I have not been able to find any online sources that give a formula for that. For the record, my interest in this is due to a memory reporting bug in .NET 8 – github.com/dotnet/runtime/issues/94846 – it would be much appreciated if you have any arguments in favor of using memory pressure for calculating the learn.microsoft.com/en-us/dotnet/api/… value on macOS (or a better suggestion, for that matter).
    – Otto G
    Commented Jan 15 at 16:14
  • @OttoG Whilst nearly all the values in my script come from vm_stat, Memory Pressure is not. It uses the command memory_pressure. How it is calculated internally, I have no idea. Windows memory load has no direct relevance to macOS - memory management is very different.
    – Gilby
    Commented Jan 15 at 22:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .