Why doesn't iOS have automatic garbage collection? - iphone

When developing with Objective-C on iOS, memory management currently must be performed by the developer. Some of the other mobile platforms use automatic garbage collection to remove the need for managing memory.
What could be the reasons why garbage collection is not used on the iOS devices?

The problem with garbage collection is that memory usage grows until it's collected, so there might be more memory allocated than necessary. That's bad for devices with restricted memory and no option to swap.
When the garbage collector runs, it scans the heap to find memory that's no longer being used, and that's an expensive process, that will slow down your device until is has completed.

At WWDC 2011, Apple explained that they didn't want garbage collection on their mobile devices because they want apps to be able to run with the best use of the provided resources, and with great determinism. The problem with garbage collection is not only that objects build up over time until the garbage collector kicks in, but that you don't have any control over when the garbage collector will kick in. This causes non-deterministic behavior that could lead to slowdowns that could occur when you don't want them.
Bottom Line: You can't say, "OK. I know that these objects will be freed at X point in time, and it won't collide with other events that are occurring."

The main reason is probably memory load and performance. Reference counting has a smaller memory profile in that it allows the amount used by the application grow much more than reference counting. Also, there is a performance issue in that when the garbage collector runs, other threads have to be stopped. This is not really a huge issue on Macintoshes with fast multicore processors but could cause the UI to stutter on mobile devices.
The debate may be moot soon anyway. Clang / LLVM has just had a new feature added called automatic reference counting. This leverages the analysing capability to automatically put in the retains, releases and autoreleases so that the programmer doesn't have too.

Related

Ionic 2 / Ionic 3 - Garbage Collection

I'm trying to get a better understanding of ionic2 and ionic3.
How does the Garbage Collection work in ionic?
What gets cached and when?
How can we clear this cache?
How do we set up elements for G.C.?
Do we even need to setup elements for G.C?
Can we/Do we need to setup pages for G.C.?
Like seen in this picture (source):
Some of the memory gets G.C'd when going to a new page. However the memory is still significantly higher than before any video had been played.
OK I'm gonna give this one a try:
Ionic itself has not much to do with GC, there are no scheduled runs of a task that cleans up behind you. The only thing ionic (or more specifically the dev team behind ionic) has to do is to design and implement their UI components in way they do not eat up too much memory and also realease unused memory. Especially with Virtual-Scroll there have been issues with memory-leaks and so on.
So lets go a level deeper: Angular! Same point as with ionic. The devs of Angular are responsible for how much memory is used by their framework. But Angular provides a very useful method ngOnDestroy(). Why is this method important to you as an app developer? Because it gives you the chance to clean up behind yourself. This method is called just before your component is destroyed, what does that mean? You do not need your allocated objects, arrays, video-elements (set src='' and then call load()), etc. anymore and you can release the memory. This and this are good reads on how to free memory. However as the docs for onDestory() mention you only have to release memory that is not cleaned up by the automic GC (subscriptions, media-elements, ...). Which brings us to the next level:
Javascript/Browser: This is where the "real" GC happens. Javascript uses a mark-and-sweep garbage collecotor (all modern browsers ship with one), you can read about it here. It runs every now and then and releases every object that is unreachable/not referenced anymore, to explicitly mark an object for GC use the delete keyword. The following image visualizes the mark and sweep process:
Image taken from this article, it explains how javascript memory management works in very great detail, I strongly
recommend reading it.
And of course you always have the native GC of Java/Obj-C which cleans up the native part of the app.

If malloc() is considered causing "dirty memory", is free() cleaning it up?

I've heard rumors that calling malloc leads to so called "dirty memory", which you can see in the VM Tracker instrument.
Now, rumors also say one must try to keep the amount of dirty memory as low as possible. But what they didn't talk much about was how to undirty it again.
Sometimes there's no other option than using malloc(). Heck, I love malloc(). For example when creating audio sources for OpenAL, one must malloc() a lot of data.
So: When my app calls malloc() and free() all over the place, I always believed that's fine. Am I having a huge problem when doing that? Or will free() always "clean it up"? I'm a bit confused because some very big guys at a very big company warned that malloc() must be avoided as much as possible, because of this dirty memory problem.
Maybe someone can un-confuse me about that.
I seriously doubt this is true. All memory allocation in Cocoa is eventually done via malloc. so sayeth Apple's Memory Usage Performance Guidelines. Quoting from that document:
Because memory is such a fundamental resource, Mac OS X and iOS both
provide several ways to allocate it. Which allocation techniques you
use will depend mostly on your needs, but in the end all memory
allocations eventually use the malloc library to create the memory.
Even Cocoa objects are allocated using the malloc library eventually.
I don't know about your big guys at your big company, but I've known big guys at big companies that didn't know squat. Just sayin'. Documentation trumps rumors every time. :)
I don't know what they mean by "dirty" and "clean". Possibly they are referring to the problem of fragmentation. Doing lots of allocs and frees can cause fragmentation problems, but it really depends on the usage patterns and block sizes you are allocating. In general, don't worry about using malloc and free. If you a real reason to avoid the standard allocator, you can use your own allocator. Then you just call malloc once for a huge block that you can use as the basis of your custom allocator.
If you malloc and free same size memory blocks multiple times, the memory will be reused instead of accumulating dirty VM pages. So it's perfectly safe as long as you know the max of all possible fragment sizes ever allocated by your app at any one time, and that keeps your app under the OS kill limit.
You are correct - free() will simply clean it up again.

What do "Dirty" and "Resident" mean in relation to Virtual Memory?

I dropped out of the CS program at my university... So, can someone who has a full understanding of Computer Science please tell me: what is the meaning of Dirty and Resident, as relates to Virtual Memory? And, for bonus points, what the heck is Virtual Memory anyway? I am using the Allocations/VM Tracker tool in Instruments to analyze an iOS app.
*Hint - try to explain as if you were talking to an 8-year old kid or a complete imbecile.
Thanks guys.
"Dirty memory" is memory which has been changed somehow - that's memory which the garbage collector has to look at, and then decide what to do with it. Depending on how you build your data structures, you could cause the garbage collector to mark a lot of memory as dirty, having each garbage collection cycle take longer than required. Keeping this number low means your program will run faster, and will be less likely to experience noticeable garbage collection pauses. For most people, this is not really a concern.
"Resident memory" is memory which is currently loaded into RAM - memory which is actually being used. While your application may require that a lot of different items be tracked in memory, it may only require a small subset be accessible at any point in time. Keeping this number low means your application has lower loading times, plays well with others, and reduces the risk you'll run out of memory and crash as your application is running. This is probably the number you should be paying attention to, most of the time.
"Virtual memory" is the total amount of data that your application is keeping track of at any point in time. This number is different from what is in active use (what's being used is marked as "Resident memory") - the system will keep data that's tracked but not used by your application somewhere other than actual memory. It might, for example, save it to disk.
WWDC 2013 - 410 Fixing Memory Issues Explains this nicely. Well worth a watch since it also explains some of the practical implications of dirty, resident and virtual memory.

Correct approach for checking memory allocations in Objective C

I'm wondering what would be the correct approach after executing a command that allocates memory in Objective C (I'm mainly referring to iOS apps).
My dilemma comes from the fact that checking for the success of failure of a memory allocation operation adds lots of lines of code, while wondering whether this is at all useful.
Moreover, sometimes memory allocations are obvious, such as using 'alloc', but sometimes they are taking place behind the scenes. And even if we check each and every allocation - when we find it failed - there isn't much we could actually do. So maybe the correct approach is to just let it fail and have the app crash?
Take a look at this code:
// Explicit memory allocation
NSArray a1 = [[NSArray alloc] initWithObjects:someObj, nil];
if (!a1) {
// Should we make this check at all? Is there really what to do?
}
// Implicit memory allocation
NSArray a2 = [NSArray arrayWithObjects:someObj, nil];
if (!a2) {
// should we make this check at all? Is there really what to do?
}
What in your opinion would be the correct approach? Check or not check for allocation failures? iOS developers out there - how have you handled it in your apps?
Fantasy: Every memory allocation would be checked and any failure would be reported to the user in a friendly fashion, the app would shut down cleanly, a bug report would be sent, you could fix it and the next version would be perfect [in that one case].
Reality: By the time something as trivial as arrayWithObjects: fails, your app was dead long, long, ago. There is no recovery in this case. It is quite likely that the frameworks have already failed an allocation and have already corrupted your app's state.
Furthermore, once something as basic as arrayWithObjects: has failed, you aren't going to be able to tell the user anyway. There is no way that you are going to be able to reliably put a dialog on screen without further allocations.
However, the failure happened much further before your app failed an allocation. Namely, your app should have received a memory warning and should have responded by (a) persisting state so no customer data is lost and (b) freeing up as much memory as possible to avoid catastrophic failure.
Still, a memory warning is the last viable line of defense in the war on memory usage.
Your first assault on memory reduction is in the design and development process. You should consider memory use from the start of the application development process and you must optimize for memory use as you polish your application for publication. Use the Allocations Instrument (see this Heapshot analysis write-up I did a bit ago -- it is highly applicable) and justify the existence of every major consumer of memory.
iPhone apps should register for UIApplicationDidReceiveMemoryWarningNotification notifications. iOS will send these when available memory gets low. Google iphoneappprogrammingguide.pdf (dated 10/12/2011) for more information.
That said, one general approach to the problem I've seen is to reserve a block of memory at app startup as a "cushion". In your code put in a test after each allocation. If an allocation fails, release the cushion so you have enough memory to display an error message and exit. The size of the cushion has to be large enough to accommodate allowing your hobbled app to shutdown nicely. You could determine the size of the cushion by using a memory stress tester.
This is really a tricky problem because it happens so rarely (for well-designed programs). In the PC/mini/mainframe world virtual memory virtually eliminates the problem in but the most pathological programs. In limited memory systems (like smartphones), stress testing your app with a heap monitor tool should give you a good indication of its max memory usage. You could code a high water mark wrapper routine for alloc that does the same thing.
Check them, assert in debug (so you know where/why failures exist), and push the error to the client (in most cases). The client will typically have more context - do they retry with a smaller request? fail somehow? disable a feature? Display an alert to the user, etc, etc. The examples you have provided are not the end of the world, and you can gracefully recover from many - furthermore, you need to (or ought to) know when and where your programs fail.
With the power you have in an Iphone/smartphone, the time it takes to compute a few test is ridiculous to be thinking "is it really worth checking", it is always good test and catch any failures in your code/allocations. (if you don't it sounds more like your lazy to add a few extra lines in your code.
Also "letting the app crash" gives a REALLY poor impression of your application, the user see the app close for no reason and thinks its a poor quality software.
You should always add your tests and if you can't do anything about the error then at least you should display a message before the app closes (makes the user less frustrated).
there a several options when tracking memory allocations, like catching exception. testing if the pointer returned is nil, checking the size of the list etc.
you should think of ways to let your application run in the case the allocation fails:
if it is jsut a view of your interface, display a message saying fail to load the particular view ...
if it is the main and only view, close the application gracefully with a message
...
I don't know what application you are working on but if you are short on memory, you should consider creating a system to allocate a deallocated memory as your progressing in your app, so that you always have the maximum memory available. it might be slightly slower than keeping everything cached but you app quality will improve if you suppress any force close.

Will Apple reject my iPhone application because of increasing memory usage?

I have an application with multiple views. It works pretty fine without any leaks or crashes. But when you run using performance tool for leaks, I see when I switch through multiple views and comeback to home screen, my overall size of the application gets increased. Like if its 1.53MB after visiting 4-5 different views and getting back to screen increases the consumption to 1.58MB or less but definitely greater than 1.53MB.
I tried resolving this issue but not able to figure out where I am going wrong since there are no memory leaks.
Does anyone know what could be the problem?
Will apple reject my application on this basis?
I would go back and forth between the screens many many many many times (at least one hundred times). If the memory continues to grow (linearly) during that time, you have a problem. If the memory stabilizes, you might be okay.
Definitely keep trying to fix you memory leaks. But if it's small, I doubt Apple will notice it. I mean, their own apps leak some too. You could get rejected for it, sure. But realistically, leaking a few bytes here and there shouldn't prevent an approval by itself.
(Source, 2 apps approved, one with the same issue, a tiny little memory leak I couldn't track down. I submitted it and was approved. Shortly after, I found and fixed it and released it as part of an update).
If an application has an increasing memory footprint on a known stable state for example named A after going into and coming back from state B which should have no persistent affect on state A and there is no memory leaks this problem called (as much as I know) the lingering memory.
Checklist to be sure if you have lingering memory problem:
App has no memory leaks, or no memory leaks on non-system code when profiled by Instruments.
State A and State B are individually stable states, like in state-machine.
State B has no permanent affect on State A, or it's memory. State A could be a gateway, a menu to another states like State B or State C. But Child states has no or limited info about state A and makes no change about State A.
On loop state changes starts and ends with root state for example A->B->A, A->C->A, A->B->C->A; you encounter increasing memory usage on State A. Memory usage on other child states are not important.
To spot and solve this problem profile your app with instruments. But instead of monitoring leaks, you should monitor allocations and total memory. Every time your app gets to State A, including start, take a memory snapshot. (There is a button for that :D) After snapshot go to State B, State C and use your application as it suppose to. After coming back to root state, in this example State A, take another snapshot. Instruments will show you memory allocations and difference delta in total memory between snapshots. It will also give information about for which object the memory had allocated and when if possible. If it was your code you probably will see the type of class and allocation point. Instruments can not help you about when the object should have been released but when you got the lingering object or memory, figuring out the deallocation point should be much easier.
BUT! Do not forget:
OS and Framework codes could have leaks and lingering memory problems like every OS. If you are sure that it is not your code leaking or lingering in the memory the everything is fine. That was the case in my app and it got approved(App: Tusudoku). System function often use additional memory if there is available, but they immediately release it when received memory warning. Although devices has limited memory, it is a waste if still not used, and using memory does not make memory chip to use measurably increased electrical current. Using memory to the limits for performance and immediately releasing it when someone definitely needs it, is best possible practice. These cache memories does not tend to be grove over time linearly but you should force memory warning every time app gets to root state, in this example State A. So this way you will be sure any cache memory allocated by system or frameworks will be deallocated, then you take the snapshot.
Most of the apps on the App StoreĀ® has memory leaks and other memory problems. The question is how this affect user. Non-linear lingering memory with rapidly dropping acceleration on increase velocity generally won't be a reason for rejection. Calculated the memory usage as 15MB for a perfect working app but if it worked, no problem, say that it will reach 20MB limit max ever and you are good to go. So you later fix your memory problems. Bu if your application has a linear or worse increasing memory usage and can not release that memories when needed, that will be a critical problem.
For more information about memory usage please consider reading official documentation and watching WWDC videos(That's where I learned all about memory fixes using Instruments).
There is no set in stone answer.
On the one hand is the fact that your application may have an obscure memory leak would be enough to reject it according to the posted policies.
On the other hand documents submitted to the FCC by Apple (in the AT&T+Apple vs. Google monopoly fight) give enough detail to work out just how much goes into reviewing an app - unless Apple lied the average app is reviewed by 2 people, and each of them spends around 5 minutes and 38 seconds (assuming Apple doesn't give breaks) to determine if your app passes or fails.
So the answer largely depends on if this memory leak can be discovered in the first 5 minutes of examination by some of the most overworked testers in the industry.
If you are using UIImageViews in your views, then part of the extra memory could be the caching that it does. See here.
Sometimes when we load views, and then switch to another, we leave the view around. For example, if you have a rootviewcontroller that has all the views as retained properties. Normally when you remove a subview, it is released, but not if you have it retained in your viewcontoller. As yu can see, that would add up to memory consumed, but not freed. It's not a leak, except that it gets released only when you release or remove the rootviewcontroller.
You could try to go through and find places where memory is tied up like this, or you could justify it based on the added speed of going through views without having to wait for them to reload.
In summary, it is good to know why your views and other objects consume and hold on to memory, but you may find that all those uses are justified, and you want to keep things that way. Having said that, I don't think Apple will be rejecting our app for decisions like this. If your app crashes because of memory usage, then that would get it rejected.
You're describing very typical memory usage.
If your app runs out of memory and crashes while they're testing it, they will reject it. Beyond that, you're fine.