How to respond to memory pressure notifications from GCD? - ios

I am using GCD to get memory pressure notifications.
GCD documentation describes some constants like so:
DISPATCH_MEMORYPRESSURE_WARN
The system memory pressure condition is at the warning stage. Apps
should release memory that they do not need right now.
DISPATCH_MEMORYPRESSURE_CRITICAL
The system memory pressure condition is at the critical stage. Apps
should release as much memory as possible.
Seems logical that I should free unused memory. However, in other places (man pages and source code) I find this note related to these constants:
Elevated memory pressure is a system-wide condition that applications
registered for this source should react to by changing their future
memory use behavior, e.g. by reducing cache sizes of newly initiated
operations until memory pressure returns back to normal.
However, applications should NOT traverse and discard existing caches
for past operations when the system system tem memory pressure enters
an elevated state, as that is likely to trigger VM operations that
will further further ther aggravate system memory pressure.
This confuses me. So should I free memory, or should I just stop allocating new memory?

MacOS has a virtual memory (VM) system that uses a backing store: the file system. The file system is used to hold memory that is not currently in use. When the system is running low on real memory (RAM), things in memory that are not actively being used can be written to disk and loaded back into RAM later.
iOS has a virtual memory system but not a backing store. When memory runs low the system asks apps to lower their memory footprint. If that does not free up enough memory the system will start killing apps.
The guidance you are quoting from the libdispatch headers is referring to the MacOS virtual memory system, not iOS.
On iOS an application should discard objects and reduce cache sizes when handling a memory warning. Particular attention should be paid to objects that are using dirty (non-purgeable) memory. This is memory the system can not automatically reuse on its own - it must be discarded by the application first. In a typical iOS application images (pictures) use the most dirty memory.

Related

How OS handles memory leaks

I searched quite a lot for the question but was unable to find my exact query although it seems general enough that might have been asked and answered somewhere.
I wanted to know what happens after a process causes a memory leak and terminates. In my opinion it's not big deal because of virtual memory. After all physical pages can be still allocated to other/new process even if it was causing memory leak earlier (after old process caused memory leak)
But I also read somewhere that due to memory leaks you need to restart your system, and I dont seem to understand why???
Recommended reading : Operating Systems: Three Easy Pieces
On common OSes (e.g. Linux, Windows, MacOSX, Android) each process has its own virtual address space (and the heap memory, e.g. used for malloc or mmap, is inside that virtual address space), and when the process terminates, its entire virtual address space is destroyed.
So memory leaks don't survive the process itself.
There could be subtle corner cases (e.g. leaks on using shm_overview(7) or shmget(2)).
Read (for Linux) proc(5), try cat /proc/self/maps, and see also this. Learn to use valgrind and the Address Sanitizer.
Read also about Garbage Collection. It is quite relevant.
In modern operating systems the address space is divided into a user space and an system space. The system space is the same for all processes.
When you kill a process, that destroys the user space for the process. If an application has a memory leak, killing the process remedies the leak.
However,the operating system can also allocate memory in the system space. When there is a memory leak in the operating system's allocation of system space memory, killing processes does not free it up.
That is the type of memory leak that forces you to reboot the system.

cudaMallocManaged vs cudaMalloc - Device memory limitation scenario

I understand that cudaMallocManaged simplifies memory access by eliminating the need for explicit memory allocations on host and device. Consider a scenario where the host memory is significantly larger than the device memory, say 16 GB host & 2 GB device which is fairly common these days. If I am dealing with input data of large size say 4-5 GB which is read from an external data source. Am I forced to resort to explicit host and device memory allocation (as device memory is insufficient to accommodate at once) or does the CUDA unified memory model has a way to get around this (something like, auto allocate/deallocate on need basis)?
Am I forced to resort to explicit host and device memory allocation?
You are not forced to resort to explicit host and device memory allocation, but you will be forced to handle the amount of allocated memory manually. This is because, on current hardware at least, the CUDA unified virtual memory doesn't allow you to oversubscribe GPU memory. In other words, cudaMallocManaged will fail once you allocate more memory than what is available on the device. But that doesn't mean you can't use cudaMallocManaged, it merely means you have to keep track of the amount of memory allocated and never exceed what the device could support, by "streaming" your data instead of allocating everything at once.
Pure speculation as I can't speak for NVIDIA, but I believe this could be one of the future improvements on upcoming hardware.
And indeed, one year and a half after the above prediction, as of CUDA 8, Pascal GPUs are now enhanced with a page-faulting capability that allows memory pages to migrate between the host and the device without explicit intervention from the programmer.

What's the right statistic for iOS Memory footprint. Live Bytes? Real Memory? Other?

I'm definitely confused on this point.
I have an iPad application that shows 'Live Bytes' usage of 6-12mb in the object allocation instrument. If I pull up the memory monitor or activity monitor, the 'Real Memory' Column consistently climbs to around 80-90mb after some serious usage.
So do I have a normal memory footprint or a high one?
This answer and this answer claim you should watch 'Live Bytes' as the 'Real Memory' column shows memory blocks that have been released, but the OS hasn't yet reclaimed it.
On the other hand, this answer claims you need to pay attention to that memory monitor, as the 'Live Bytes' doesn't include things like interface elements.
What is the deal with iOS memory footprint!? :)
Those are simply two different metrics for measuring memory use. Which one is the "right" one depends on what question you're trying to answer.
In a nutshell, the difference between "live bytes" and "real memory" is the difference between the amount of memory currently used for stuff that your app has created and the total amount of physical memory currently attributed to your app. There are at least two reasons that those are different:
code: Your app's code has to be loaded into memory, of course, and the virtual memory system surely attributes that to your app even though it's not memory that your app allocated.
memory pools: Most allocators work by maintaining one or more pools of memory from which they can carve off pieces for individual objects or allocated memory blocks. Most implementations of malloc work that way, and I expect that the object allocator does too. These pools aren't automatically resized downward when an object is deallocated -- the memory is just marked 'free' in the pool, but the whole pool will still be attributed to your app.
There may be other ways that memory is attributed to your app without being directly allocated by your code, too.
So, what are you trying to learn about your application? If you're trying to figure out why your app crashed due to low memory, look at both "live bytes" (to see what your app is using now) and "real memory" (to see how much memory the VM system says your app is using). If you're trying to improve your app's memory performance, looking at "live bytes" or "live objects" is more likely to help, since that's the memory that you can do something about.
Seeing as how I wrote the last answer you linked to, I'll have to stand by that. If you want a total, accurate count of the current memory usage for your application, use the Memory Monitor instrument.
For reasons that I describe in this answer, Allocations hides the memory sizes of certain elements, meaning that its memory usage totals are significantly lower than your application's in-memory size. Many people find this out the hard way when they try to get their application functional on older iOS devices. On the older hardware, you had a hard memory ceiling of ~30 MB, where if you exceeded that your application was hard-killed.
Many developers (myself included) saw that we only had ~1-2 MB of live bytes in Allocations and thought we were good, until our applications started receiving memory warnings and early terminations. If you looked at Memory Monitor, you could see the true in-memory size of these applications being >20 MB, and you could see the applications being terminated the instant they crossed the 30 MB barrier in Memory Monitor.
Therefore, if you want an accurate assessment of your total application memory usage, use Memory Monitor. Allocations is great to find out the specific objects that are in memory, particularly when you use the heap shots to find things that might be accumulating (as leaks, retain cycles, or for other reasons). Just don't trust it when determining your application's actual size in memory.
'Live bytes' means memory allocated by your code (for example by malloc), so you have access to this memory. 'Real memory' shows physical amount of memory used by your app. This include also OpenGL textures, (possibly) sounds from Open AL...
Live bytes is useful to check when you allocate and release memory in your code. Real memory is good indicator for memory optimization efficiency. And it's overhead causes 'low memory' warnings.

How to reserve memory for my application and leave a specified amount remaining?

I'm planning an application which will involve loading many pictures at one time and thus requires a large chunk of memory. For example, I might have 50 image objects created at once, taking a total of 1GB of RAM. But when the user goes to load 20 more pictures, I'd like to make sure that amount of memory is already reserved and ready.
Now this part might seem a little backwards from normal. Rather than specifying how much memory my application shall reserve, instead I need to specify how much memory to leave free for other applications, and adjust my application's memory periodically according to this specification. I must say I've never worked with reserving memory at all, and especially won't know how to leave this remaining available memory.
So for example, if the computer has 2048 MB of RAM, and the option is set to leave 50 MB free for other applications, and there is already 10MB of RAM being used by other apps, then it should reserve 2048-50-10 = 1988 MB for my app.
The trouble I foresee is suppose the user opens another application which requires 1GB. My app has to catch this and shrink its self.
Does this even sound like a feasible approach? Basically, I need to make sure there is as much memory reserved as possible at any given time, while leaving a decent amount available for other apps. Would it make a significant impact on performance if I do this, or not much at all? I might be loading and unloading images at rapid paces, and I don't want it to reserve/free this memory on demand, I want it to stay reserved.
+1 for Sertac's mentioning of how SQL Server rides the line of allocating memory it needs, but releasing memory when Windows complains.
Applications can receive Window's complaints by using the CreateMemoryResourceNotification:
hLowMemory := CreateMemoryResourceNotification(LowMemoryResourceNotification);
Applications can use memory resource notification events to scale the
memory usage as appropriate. If available memory is low, the
application can reduce its working set. If available memory is high,
the application can allocate more memory.
Any thread of the calling
process can specify the memory resource notification handle in a call
to the QueryMemoryResourceNotification function or one of the wait functions.
The state of the object is signaled when the specified
memory condition exists. This is a system-wide event, so all
applications receive notification when the object is signaled. Note
that there is a range of memory availability where neither the
LowMemoryResourceNotification or HighMemoryResourceNotification object
is signaled. In this case, applications should attempt to keep the
memory use constant.
But it's also worth mentioning that you might as well allocate memory that you need. Your operating system has a very sophisiticated set of algorithms to swap out the least used memory when memory pressure is high. You can take advantage of this by simply allocating all the memory that you need. When Windows starts to run low, it will find those pages of memory that you are using the least and swap them out to disk. (This is how a well-known reverse proxy works).
The only thing left is to decide if you want to free some images when Windows says it's running low on RAM. But if you're not using the memory, it is going to be swapped out to disk for you.
It's not realistic to account for other apps. Just ignore them. The system will page things in and out as needed. If you really wanted to do this you'd have to dynamically adapt to other processes as they start and finish. That's really not realistic. What's more it's not practical to inquire of other processes how much memory they need. Leave it all to the system.
Set a budget for your app and make sure you don't exceed it. Keep the most recently used images in memory and when you approach your memory budget throw away the least recently used images to make space.
If you are stressing the available resources then make sure you use FastMM and enable LARGE_ADDRESS_AWARE for your app so that you get 4GB address space when running on a 64 bit OS.

Memory-mapped files and low-memory scenarios

How does the iOS platform handle memory-mapped files during low-memory scenarios? By low-memory scenarios, I mean when the OS sends the UIApplicationDidReceiveMemoryWarningNotification notification to all observers in the application.
Our files are mapped into memory using +[NSData dataWithContentsOfMappedFile:], the documentation for which states:
A mapped file uses virtual memory techniques to avoid copying pages of the file into memory until they are actually needed.
Does this mean that the OS will also unmap the pages when they're no longer in use? Is it possible to mark pages as being no longer in use? This data is read-only, if that changes the scenario. How about if we were to use mmap() directly? Would this be preferable?
Memory-mapped files copy data from disk into memory a page at a time. Unused pages are free to be swapped out, the same as any other virtual memory, unless they have been wired into physical memory using mlock(2). Memory mapping leaves the determination of what to copy from disk to memory and when to the OS.
Dropping from the Foundation level to the BSD level to use mmap is unlikely to make much difference, beyond making code that has to interface with other Foundation code somewhat more awkward.
(This is not an answer, but it would be useful information.)
From #ID_AA_Carmack tweet,
#ID_AA_Carmack are iOS memory mapped files automatically unmapped in low memory conditions? (using +[NSData dataWithContentsOfMappedFile]?)
ID_AA_Carmack replied for this,
#KhrobEdmonds yes, that is one of the great benefits of using mapped files on iOS. I use mmap(), though.
I'm not sure that is true or not...
From my experiments NSData does not respond to memory warnings. I tested by creating a memory mapped NSData and accessing parts of the file so that it would be loaded into memory and finally sending memory warnings. There was no decrease in memory usage after the memory warning. Nothing in the documentation says that a memory will cause NSData to reduce real memory usage in low memory situations so it leads me to believe that it does not respond to memory warnings. For example NSCache documentation says that it will try and play nice with respect to memory usage plus I have been told it responds to the low memory warnings the system raises.
Also in my simple tests on an iPod Touch (4th gen) I was able to map about 600 megs of file data into virtual memory use +[NSData dataWithContentsOfMappedFile:]. Next I started to access pages via the bytes property on the NSData instance. As I did this real memory started to grow however it stopped growing at around 30 megs of real memory usage. So the way it is implemented it seems to cap how much real memory will be used.
In short if you want to reduce memory usage of NSData objects the best bet is to actually make sure they are completely released and not relying on anything the system automagically does on your behalf.
If iOS is like any other Unix -- and I would bet money it is in this regard -- pages in an mmap() region are not "swapped out"; they are simply dropped (if they are clean) or are written to the underlying file and then dropped (if they are dirty). This process is called "evicting" the page.
Since your memory map is read-only, the pages will always be clean.
The kernel will decide which pages to evict when physical memory gets tight.
You can give the kernel hints about which pages you would prefer it keep/evict using posix_madvise(). In particular, POSIX_MADV_DONTNEED tells the kernel to feel free to evict the pages; or as you say, "mark pages as being no longer in use".
It should be pretty simple to write some test programs to see whether iOS honors the "don't need" hint. Since it is derived from BSD, I bet it will.
Standard virtual memory techniques for file-backed memory says that the OS is free to throw away pages whenever it wants because it can always get them again later. I have not used iOS, but this has been the behavior of virtual memory on many other operating systems for a long time.
The simplest way to test it is to map several large files into memory, read through them to guarantee that it pages them into memory, and see if you can force a low memory situation. If you can't, then the OS must have unmapped the pages once it decided that they were no longer in use.
The dataWithContentsOfMappedFile: method is now deprecated from iOS5.
Use mmap, as you will avoid these situations.

Resources