I'm a little bit confused in comparing the virtual memory system behaviour in OSX/iOS to that of Windows. The Windows VirtualAlloc() related functions and their behaviour regards reserving and the actual memory commit and de-commit are fairly straight forward.
For OSX which is not well discussed I have been looking at mach_vm_allocate(), mach_vm_map() etc. For example if I wanted to create a set of cross platform functions to expose common virtual memory functionality between Windows and OSX/iOS how would I managed the difference between commit/de-commit on OSX as opposed to Windows?
As I am not sure I understand if you can reserve virtual address range and commit it as a separate action like on Windows? From my understanding mach_vm_allocate() is similar to VirtualAlloc() with MEM_COMMIT | MEM_RESERVE and also trying to compare which is actually a better designed mechanism if any is confusing.
Possibly I need better understanding of how the page managers function in OSX.
On Windows even if you commit a region I suspect it may not actually back it with physical memory until you try accessing it unless perhaps there is currently abundant memory - and that it just guarantees swap file backing when modified.
On OSX I am not sure how to de-commit regions but still keep the address range reserved? For example this sort of behaviour is useful in a 64bit programs (Which I am mostly interested in) to reserve a large virtual address range for an arena/stack/linear allocator with rewind ability - that requires being able to commit and de-commit the ends of regions. In Windows it is obvious how to produce such behaviour but in OSX I don't quite understand how to efficiently replicate it.
EDIT:
I just found this:
Reserve memory on OS X
That is relevant to my question but surely mmap() goes through the equivalent mach_vm_*() system calls?
EDIT2:
Typical I now find these:
Does mmap with MAP_NORESERVE reserve physical memory?
How can I reserve virtual memory in Linux?
But it still perhaps doesn't clear up how to de-commit the way I would like too - but off to google some more on mmap() ANON stuff - and possibly see if I can find the mmap() source code for OSX (?).
(For sure someone will say use mmap() as it works on linux too if I can figure out the de-commit issue but still I am curious how that gets routed through the mach_vm_*() calls...)
EDIT3:
I found mremap() which together with mmap() looks useful! Obviously using PROT_NONE, MAP_NORESERVE with mmap() look interesting also. But I am still unsure how you can de-commit regions but still keep the address range preserved as mremap() doesn't seem to be able to take MAP_NORESERVE to ditch the swap file backing?
EDIT4:
I found this regards de-commits: https://bugzilla.mozilla.org/show_bug.cgi?id=670596. Which discusses the behaviour on OSX and Linux regards mprotect(addr, len, PROT_NONE) and madvise(). ..
EDIT5: (!)
Digging through my Mac header files I find for madvise():
#define MADV_WILLNEED POSIX_MADV_WILLNEED
#define MADV_DONTNEED POSIX_MADV_DONTNEED
#define MADV_FREE 5 /* pages unneeded, discard contents */
#define MADV_ZERO_WIRED_PAGES 6 /* zero the wired pages that have not been unwired before the entry is deleted */
#define MADV_FREE_REUSABLE 7 /* pages can be reused (by anyone) */
#define MADV_FREE_REUSE 8 /* caller wants to reuse those pages */
#define MADV_CAN_REUSE 9
So I am guessing MADV_FREE_REUSE should be preferred usage for de-commits?
EDIT6: I have asked the question on the iOS/OSX developer forums and in the meantime I have come across these that may be helpful to other people wondering the same thing:
http://lists.apple.com/archives/PerfOptimization-dev/2009/Apr/msg00024.html http://markmail.org/message/yqwqd3zuawz6v5dd
Also this:
http://fxr.watson.org/fxr/source/bsd/kern/kern_mman.c?v=xnu-1228;im=bigexcerpts#L824
Which seems like the key is mmap(), and madvise(), or mach_vm_allocate() and mach_vm_behavior_set() with flag VM_BEHAVIOR_DONTNEED.
Will report back for the benefit of others after experimenting with this...
EDIT7:
Latest for now OSX 10.9 source code for mmap() and madvise() I think: http://www.opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/kern/kern_mman.c
Seems to confirm mach_vm_behavior_set()
EDIT8:
Ok now so far as I can tell from OSX 10.9:
http://www.opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/vm/vm_map.c
I should use mach_vm_allocate() and vm_map_behavior_set() with the (suggestive) flags being roughly equivalent in Windows VirtualAlloc() parlance too:
VM_BEHAVIOR_WILLNEED => Commit address range
VM_BEHAVIOR_DONTNEED => Decommit address range
VM_BEHAVIOR_FREE => Decommit and completely release address range(?)
But I am not sure what these mean exactly still(?):
VM_BEHAVIOR_REUSABLE
VM_BEHAVIOR_REUSE
VM_BEHAVIOR_CAN_REUSE
I am hoping from confirmation from apple on the preferred usage pattern but I guess I am close to answering my own question with the above...
This is the first time I have had the pleasure to dig through some very clean open source code :-)
Apple is adding virtual memory swap to iPadOS 16, allowing apps on the recent iPad Pro and iPad Air models to use free and available storage as extra memory for demanding workloads. With iPadOS 15, certain apps can use up to 12GB of memory on the highest-end M1 iPad Pro which has 16GB of total RAM.
To keep your information safe, macOS uses secure virtual memory. Virtual memory is a technique computers use to temporarily move data from random-access memory (RAM) to the hard disk if the amount of available RAM is limited.
The iOS virtual memory model does not include swap memory, which means that, unlike with desktop apps, the disk cannot be used to page memory. The end result is that the apps are restricted to available RAM, which is used not only by the app in ...
NOTE3: The MEM_COMMIT flag will commit pages on a page size boundary, while using MEM_RESERVE or MEM_RESERVE|MEM_COMMIT will reserve or reserve+commit pages on a boundary greater than the page size, usually 64K on all versions of Windows since today. You can get this number by calling GetSystemInfo() .
I use method below on OS X and iOS:
char* m_base = 0;
unsigned m_offset = 0;
unsigned MAX_SIZE = 1024 * 1024 * 10; // 10 Mb
// init and reserve memory
kern_return_t err = vm_allocate(mach_task_self(), (vm_address_t*)&m_base, MAX_SIZE, VM_FLAGS_ANYWHERE);
// alloc memory size
size = (size + roundToPageSize - 1) & ~(roundToPageSize - 1);
char* address = m_base + m_offset;
kern_return_t err = vm_allocate(mach_task_self(), (vm_address_t*)&address, size, VM_FLAGS_FIXED|VM_FLAGS_OVERWRITE);
m_offset += size;
// now address points to allocated memory in reserved space
// dealloc and return to system memory size
size = (size + roundTo - 1) & ~(roundTo - 1);
char* address = m_base + m_offset - size;
kern_return_t err = vm_deallocate(mach_task_self(), (vm_address_t)address, size);
m_offset -= size;
// deinit
kern_return_t err = vm_deallocate(mach_task_self(), (vm_address_t)m_base, MAX_SIZE);
m_base = 0;
m_offset = 0;
There's no exact analog. In Unix-style OSes, allocated memory can be accessed and that will cause it to be associated with physical memory or swap. But there's not always a guarantee that allocated memory has sufficient swap file space set aside for it. So, the system can fail to associate physical memory if it can't swap something else out to free up the RAM.
Both vm_allocate()
and mmap()
reserve an address range. They also both make it legal for the process to access the addresses in that range, at which point the pages will be mapped to physical memory if necessary. However, on OS X, I don't believe that either function reserves backing storage (swap) for the address range.
If you allocate some space and then access it, causing it to be mapped to RAM or swap, and you want to return it to just being allocated but clear the backing in RAM or swap, I believe that a second call to mmap()
with MAP_FIXED
will do that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With