Class IosMemory
iOS VhMemory implementation. It reads on demand (no background sampler, no cached global):
each call issues fresh mach/Foundation queries and returns the live values. The process footprint comes
from mach TASK_VM_INFO (phys_footprint — the number the ~52 MB Network Extension jetsam limit is
enforced on); total system memory from NSProcessInfo.PhysicalMemory.
public sealed class IosMemory : VhMemory
- Inheritance
-
IosMemory
- Inherited Members
Remarks
A host installs it as Instance via Install() at init — in both
processes: the app (IosDevice constructor) and the extension (IosVpnService.StartTunnel) — and
each reports its OWN process's footprint, so it works wherever it's asked. Consumers (the iOS QUIC download
brake in IosQuicStream and the TCP stack's +CONN/-CONN log annotation) then read live values without any
dependency on this class. The gated ext-mem.log probe (IosMemoryMonitor, the extension's) also reads the
fuller iOS-specific breakdown via TryRead(out FootprintInfo) on its own thread. System-wide "used" memory is left
null here (it needs host_statistics, not worth the native struct parsing); a non-iOS host can
fill it from GC.GetGCMemoryInfo().
Methods
GetInfo()
Returns the current memory snapshot. Implementations must not throw (report unavailable
figures as null instead), because consumers read this on hot paths. Read a specific figure via
the returned struct, e.g. VhMemory.Instance.GetInfo().ProcessFootprintMb.
public override VhMemoryInfo GetInfo()
Returns
- VhMemoryInfo
The snapshot, with null for each figure the platform cannot report.
Remarks
The footprint is this process's, read fresh; the total is the device's, read once; the used memory is not read.
Install()
Installs an IosMemory as Instance, unless one is already installed ("if not created"). Idempotent; call once from each process's init (the app device / the extension service). A host may override Instance afterwards.
public static void Install()
ReadRegionCensus()
Walks the task's VM regions and buckets dirty+compressed bytes by VM user tag — the ground truth for which native owner (malloc size-class, network buffers, stacks, GC maps at tag0) holds the footprint. Returns a single log-line payload; never throws.
public static string ReadRegionCensus()
Returns
- string
The census, as one log line's payload.
Remarks
vm_region_submap_info_64 is pack(4); the offsets below (user_tag@20, resident@24, swapped@32, dirtied@36, is_submap@48) were verified on-device (iPhone 11, arm64, 16 KB pages) in the 2026-07 diagnostic runs. infoCnt 19 = V2 count; falls back to 16 (V0) for older kernels.
TryRead(out FootprintInfo)
Reads a fresh phys_footprint snapshot. Returns false if the mach call fails.
public static bool TryRead(out IosMemory.FootprintInfo info)
Parameters
infoIosMemory.FootprintInfoThe reading, or the default where the call fails.
Returns
TryReadAvailableMemory()
Bytes this process can still allocate before jetsam (os_proc_available_memory). -1 on failure;
0 can mean either "no budget applies" or "budget exhausted" — read it together with the footprint.
public static long TryReadAvailableMemory()
Returns
- long
The bytes, or -1 where they cannot be read.
TryReadBootTimeUtc()
Device boot time (UTC) from kern.boottime. Newer than a log line = the device rebooted.
public static DateTime? TryReadBootTimeUtc()
Returns
TryReadFileDescriptorCount()
Returns the number of open process file descriptors, including sockets.
public static int TryReadFileDescriptorCount()
Returns
- int
The count, or -1 where it cannot be read.
TryReadFileDescriptorSummary()
Buckets the open file descriptors by type (sock/vnode/kqueue/pipe/...), highest count first. Names a fd leak's owner class at a glance (e.g. leaked SQLite connections are vnodes, leaked sockets are sock). Returns "?" on failure.
public static string TryReadFileDescriptorSummary()
Returns
- string
The summary, or
?where it cannot be read.