What are the best practices, tricks, and tutorials for using XCode's performance tools, such as the Leak Monitor and the CPU sampler, for someone trying to debug and enhance performance of an iPhone application?
Thanks!
It depends entirely on the application and on what you are trying to do. Are you trying to optimize the whole application or are you focused on a particular problem area? Are you trying to reduce memory usage, reduce CPU usage, and/or make the app more responsive?
Before you start the performance analysis, use the static analyzer to analyze your code. It will often find memory management problems that would lead to leaks that would cause your app to potentially crash on the device.
Once all of the analyzer identified problems have been fixed, the best approach is to start by identifying perceived performance problems. That is, focus on performance problems that the user would notice. Then analyze those. If you can get away with it, do the analysis on the app running in the simulator as the turnaround time is faster.
If the problem is one of bloat, use Object Alloc and Leaks to figure out why.
If it is one of laggy/sluggish behavior, use the CPU tools to figure out where the cycles are going. Keep in mind, though, that sluggish behavior may not be because of CPU usage, but may be because the main event loop is blocked by something, most likely incorrect concurrency patterns. In that case, you'll see all samples on the main thread in some kind of a lock or wait function.
Beyond that, you'll need to identify specific scenarios to yield specific answers.
use instruments in that use
object allocation
activity monitor,
leaks
memoer monitor
and test your app
Related
I am developing an app for the iPhone and am encountering some memory management issues. During execution of the app the live bytes continuously increase without bound. I have tried to track down the issue within my code but cannot seem to find anything that would cause the live bytes to increase so dramatically. The one thing that I have noticed during execution is that the allocation for CFString(Immutable) increase the most rapidly and never decrease or stay constant. Does anyone have any idea why this may be happening? All that the app is doing during this execution is populating a table view from a local array or strings, then downloading another array of string objects and populating a different table view. I am using ARC.
Given the lack of anything concrete to go on, I'll give you somewhat general counsel:
See Finding leaks with Instruments for guidance on how to use Instruments to find leaks.
For specific advice how to go from you allocations, to a more meaningful analysis of the source of those allocations, see point #4 of this Stack Overflow answer. In short, highlight one of your unexplained jumps in allocations, set the bottom window to show you the call tree, hide system libraries, and see in which of your routines the memory is being consumed.
Also, don't overlook the static analyzer, which is especially important if you don't use ARC, or if you use any Core Foundation calls.
Are you doing anything with Core Foundation functions? If so, you obviously need to know that you either have to explicitly transfer ownership to ARC (with either CFBridgingRelease or __bridge_transfer) or manually call CFRelease. The static analyzer of my prior point would point this out to you, though.
I am using instrument "Allocations" to monitor the app's memory. The vm tracker statistics puzzled me. Why there is so much dirty memory(for my app, reach 32M), I googled about this and knew that the dirty memory should be clear first when app received memory warning in background mode.
Could you tell me about the meaning of vm checker statistics? And how can I handle the dirties:VM_ALLOCATE, Core Animation.
Thanks in advance!
There is often very little you can do about VM usage directly; much of that will be due to use of various system APIs, etc...
Your time will be far more productively spent by focusing on the objects in the Allocations instrument itself and work towards both eliminating any leaks (accretion, too, not just leaks) and reducing allocation bandwidth.
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.
My app crashes after about 20 minutes with status 101 (Out of Memory, I believe)
Debugging using Instruments - ObjectAlloc and Leaks gives me no clues. The ObjectAlloc graph stays at a nice constant level of around 1 million bytes (1MB), as does the Net # of allocations. I have got rid of all leaks.
I thought it could be something to do with number of threads, but graphing these in ObjectAlloc also shows them to be constant.
Can anyone point me in the direction of another tool, or another avenue of investigation?
Fix everything Clang finds. LLVM Clang Static Analysis
Remember that objects allocated by the system (and that includes things like images and sounds) don't get tracked in Instruments (although the top level retain counts do, of course). So it's feasable that you're loading images, say, which won't contribute much to your memory usage as show, but can drain a lot of actual memory!
If none of this strikes any chords, you could try the subtractive debugging approach - (take a copy of your project) cut out chunks of functionality until the problem goes away or you get the smallest possible thing that reproduces it. That should at least help you to find where the bottleneck is. Admittedly this will be hard (a) because you'll have to wait 20 minutes or so every time you test (but if you make this a background procedure it's not so bad) and (b) because the nature of memory problems is that there may not be one single cause, but a critical mass of smaller causes.
Good luck!
My experiences with Object Alloc have not been that great. It does not always give you the actual memory used by your application.
Instead, use Object Alloc with Activity Monitor. Make sure you use the "Physical Memory Free" and "Physical Memory used" options in the activity monitor. That will tell you exactly how much memory your application is using.
What do you mean by "nice level". It does not rise over time, at all? How much memory total - it could just be the phone needs some memory for some other app and yours is a little too big to stay up.
The error code 101 means that iPhone OS force quit your app. If you're using UIImageViews in your application, be sure to manage the memory on them. I've found that once my application goes over 10/12 MB, the iPhone terminates it.
If you're not using any image views (or large images), then your backend code is eating up too much space.
All I can say is you need to look at your allocation more carefully and manage what views you keep in memory at any one time.
Run your application in Instruments (Run -> Start with Performamce Tool -> Leaks) to see where your memory is getting allocated.
Hope this helps!
Need advice on how to debug this. I'm new with limited environments and have no previous embedded or smart phone programming experience so I could use some clues.
Already aware of:
Instruments, Clanger Static Analysis, manual code review, etc. Instruments seems to be very helpful in general but quite time consuming and freezes up a lot of the time! Clanger has also helped me a lot as well. It seems like I'm just consuming too much memory in general and I'm wondering what a good strategy is. Do I release some top-level objects? Is there a 'preferred strategy'?
Just wondering if anyone has tackled this successfully and if they have any other suggestions? Thanks all.
There are a lot of good articles for memory management in an iPhone app. Here are some useful links.
http://iosdevelopertips.com/objective-c/memory-management.html
http://kosmaczewski.net/2009/01/28/10-iphone-memory-management-tips/
https://cocoa-touch.blogspot.com/2008/09/memory-management-on-iphone.html
Things you should in general take care of
Release any variables which you do not need
Always handle didReceiveMemoryWarning and release any variables not in use
Stop any memory-heavy processes in applicationDidReceiveMemoryWarning like audio/video playing, UIImagePickerController etc
EDIT
This does not apply any more. imageNamed: had caching issues prior to 3.x OS versions. The issue does not exist any more and you should use imageNamed: (makes implementing retina display easier)
Do NOT use imageNamed: to create UIImage objects.
Basically you're receiving this warning because (unsurprisingly) the iPhone is dangerously low on memory. This can generally be for one of two reasons;
You have a memory leak.
You are allocating far too many objects and need to revisit your design.
For the first one you should run instruments and examine your memory allocations. This can really slow down your app (and requires additional memory) so try testing areas of your app one at a time. E.g. if you have multiple views switch between them a couple of times.
For the second you will have to examine things you are doing that could result in large memory allocations. For example if you're writing a Flickr browser you might need to cut down the number of images you have loaded at anyone time, or free up some unused ones when you receive this warning.
These are about the only general rules I can suggest without knowing more about your app.
Unfortunately there's no real way (that I know of) to get figures for current memory allocation from the iPhone OS. This makes it really difficult to isolate the areas of your application that are inadvertently memory hungry.