I'd like to reproduce a crash bug of my app under memory pressure, so I want to alloc much memory when my app launch and expect it under memory pressure. I use the code below:
void *p[300];
NSInteger allocatedMB = 0;
for (int i = 0; i < 300; i++) {
p[allocatedMB] = malloc(1024 * 1024);
memset(p[allocatedMB], 0, 1024 * 1024);
allocatedMB += 1;
}
I expect to alloc 300M memory, and never free it. but I always get this result on my iPhone:
It alloc only 150M and later free it to only 71M. But it appear as I expect when in simulator:
I'm not quite clear about this, any answer will be appreciated about why and how I can do this on my iPhone!
The memory allocated by malloc is not autoreleased. If you do not call free, it will leak. Watch both of these two scenarios in Instruments' "Allocations" tool and you'll see heap spike up by 300 mb and not come down (in both device and simulator).
The memory view in the Xcode debugger is apparently not showing you everything. In fact, I can't get it to show me the 300mb in either simulator or device. But Instruments shows it to me. (Though, when I run Instruments on device Allocations tool works, but Leaks tool does not. This problem has been reported by others, too.)
Most likely the code is being optimized out, since the compiler can prove p is never accessed, and therefore it doesn't actually need to call memset.
Try making the pointers volatile: void volatile *p[300];
Related
I am using ARC for my iOS project and am using a library called SSKeychain to access/save items to the keychain. I expect my app to access keychain items once every 10 seconds or so (to access API security token) at peak load and as such I wanted to test this library to see how it handles when called frequently. I made this loop to simulate an insane amount of calls and noticed that it bleeds a significant amount (~75 mb) of memory when run on an iPhone (not simulator):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
dispatch_async(dispatch_get_main_queue(), ^{
NSUInteger beginMemory = available_memory();
for (int i = 0; i < 10000; ++i) {
#autoreleasepool{
NSError * error2 = nil;
SSKeychainQuery* query2 = [[SSKeychainQuery alloc] init];
query2.service = #"Eko";
query2.account = #"loginPINForAccountID-2";
query2.password = nil;
[query2 fetch:&error2];
}
}
NSUInteger endMemory = available_memory();
NSLog(#"Started with %u, ended with %u, used %u", beginMemory, endMemory, endMemory-beginMemory);
});
return YES;
}
static NSUInteger available_memory(void) {
// Requires #import <mach/mach.h>
NSUInteger result = 0;
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size) == KERN_SUCCESS) {
result = info.resident_size;
}
return result;
}
I am using SSKeychain which can be found here. This test bleeds about ~75 mb of memory regardless if things are actually stored on the keychain.
Any ideas what is happening? Is my testing methodology flawed?
I ran your code under the Leaks Instrument and this is what I saw from the Allocations track -
Which is what you would expect - a lot of memory allocated during the loop and then it is released.
Looking at the detail you see -
Persistent bytes on the heap of 2.36MB - This is the memory actually used by the app 'now' (i.e. after the loop with the app 'idling')
Persistent objects of 8,646 - again, the number of objects allocated "now".
Transient objects 663,288 - The total number of objects that have been created on the heap over the application lifetime. You can see from the difference between transient and persistent that most have been released.
Total bytes of 58.70MB - This is the total amount of memory that has been allocated during execution. Not the total of memory in use, but the total of the amounts that have been allocated regardless of whether or not those allocations have been subsequently freed.
The difference between the light and dark pink bar also shows the difference between the current 'active' memory use and the total use.
You can also see from the Leak Checks track that there are no leaks detected.
So, in summary, your code use a lot of transient memory as you would expect from a tight loop, but you wouldn't see this memory use in the normal course of your application execution where the keychain was accessed a few times every second or minute or whatever.
Now, I would imagine that having gone to the effort of growing the heap to support all of those objects, iOS isn't going to release that now freed heap memory back to the system straight away; it is possible that your app may need a large heap space again later, which is why your code reports that a lot of memory is in use and why you should be wary of trying to build your own instrumentation rather than using the tools available.
You should use Instruments to figure out where/what is causing a leak. Its a very good tool to know how to use.
This article is a little dated but you should get the basic gist.
Ray Wenderlich - Instruments
Going off of Paulw11's comment I stumbled across this,
From NSAutoreleasePool Class Reference:
The Application Kit creates an autorelease pool on the main thread at
the beginning of every cycle of the event loop, and drains it at the
end, thereby releasing any autoreleased objects generated while
processing an event.
So when you check it with instruments make sure the event loop has had time to finish. Maybe all you need to do is let the program keep running and then pause the debugger and check instruments again.
I'm doing some research on how iPhone manage the heap and stack but it's very difficult to find a good source of information about this. I'm trying to trace how a password is kept in memory, even after the NSString is deallocated.
As far as I can tell, an iPhone will not clear the memory content (write zeros or garbage) once the release count in ARC go down to 0. So the string with the password will live in memory until that memory position is overridden.
There's a debug option in Xcode, Malloc Scribble, to debug memory problems that will fill deallocated memory with 0x55, by enabling/disabling this option (and disabling Zombies), and after a memory dump of the simulator (using gcore) I can check if the content has been replaced in memory with 0x55.
I wonder if this is something that can be done with the Apple Store builds, fill deallocated memory with garbage data, if my assumption that iPhone will not do that by default is correct or not, or if there's any other better option to handle sensitive data in memory, and how it should be cleared after using it (Mutable data maybe? write in that memory position?)
I don't think that there's something that can be done on the build settings level. You can, however, apply some sort of memory scrubbing yourself by zeroing the memory (use memset with the pointer to your string).
As #Artal was saying, memset can be used to write in a memory position. I found this framework "iMAS Secure Memory" that can be useful to handle this:
The "iMAS Secure Memory" framework provides a set of tools for
securing, clearing, and validating memory regions and individual
variables. It allows an object to have it's data sections overwritten
in memory either with an encrypted version or null bytes
They have a method that it should be useful to clear a memory position:
// Return NO if wipe failed
extern inline BOOL wipe(NSObject* obj) {
NSLog(#"Object pointer: %p", obj);
if(handleType(obj, #"", &wipeWrapper) == YES) {
if (getSize(obj) > 0){
NSLog(#"WIPE OBJ");
memset(getStart(obj), 0, getSize(obj));
}
else
NSLog(#"WIPE: Unsupported Object.");
}
return YES;
}
I am running my iOS App on iPod touch device and I get memory warnings even if the total allocation peak is only 7 MB as shown below (this happens when the Game Scene is pushed):
What I find strange is that:
the left peak (at time 0.00) corresponds to 20 MB of memory allocated (Introduction Scene) and despite this DOES NOT give any memory warning.
the central peak (at time 35.00) corresponds to raughly 7 MB of memory allocated (Game Scene is being pushed) and DOES give memory warning.
I do not understand why I get those warnings if the total memory is only 7 MB. Is this normal? How can I avoid this?
Looking at the allocation density we can see the following schema, which (to me) does not show much difference between the moment when the Intro Scene is being pushed (0.00) and the moment in which the Game Scene is being pushed (35.00). Being the density peaks similar I would assume that the memory warnings are due to something else that I am not able to spot.
EDIT:
I have been following a suggestion to use "Activity monitor" instead but unfortunately my App crashes when loading the Game Scene with only 30 MB of memory allocated. Here is the Activity monitor report.
Looking at the report I can see a total real memory usage sum of about 105 MB. Given this should refer to RAM memory and given my model should have 256 MB of RAM this should not cause APP crashes or Memory leaks problems.
I run the Leak monitor and it does not show any leak on my App. I also killed all the other apps.
However, analyzing the report, I see an astonishing 167 MB of Virtual Memory value associated to my App. Is this normal? What does that value mean? Can this be the reason for the crash? How can I detect which areas of my code are responsible for this?
My iPod is a 4th Generation model with 6.4 GB of capacity (memory) and only 290 MB of memory free. I am not sure if this somehow effects the Virtual Memory paging performance.
EDIT 2: I have also looked more at SpringBoard and its Virtual Memory usage is 180 MB. Is this normal? I found some questions/answers that seem to suggest that SpringBoard is responsible for autoreleasing objects (it should be the process for managing the screen and home botton but I am not sure if it has also to do with memory management). Is this correct?
Another note. I am using ARC. However I am not sure this has to do much with the issue as there are no apparent memory leaks and XCode should convert the code adding release/dealloc/retain calls to the compiled binary.
EDIT 3: As said before I am using ARC and Cocos2d (2.0). I have been playing around with the Activity monitor. I found out that if I remove the GameCenter authentication mechanism then the Activity Monitor runs fine (new doubt: did anyone else had a similar issue? Is the GameCenter authentication view being retained somewhere?). However I noticed that every time I navigate back and forwards among the various scenes prior the GameScene (Initial Scene -> Character Selection -> Planet Selection -> Character Selection -> Planet Selection -> etc.. -> Character Selection ..) the REAL MEMORY usage increases. After a while I start to get memory warnings and the App gets killed by iOS. Now the question is:
-> am I replacing the scenes in the correct way? I call the following from the various scene:
[[CCDirector sharedDirector] replaceScene: [MainMenuScene scene]];
I have Cocos2d 2.0 as static library and the code of replaceScene is this:
-(void) replaceScene: (CCScene*) scene
{
NSAssert( scene != nil, #"Argument must be non-nil");
NSUInteger index = [scenesStack_ count];
sendCleanupToScene_ = YES;
[scenesStack_ replaceObjectAtIndex:index-1 withObject:scene];
nextScene_ = scene; // nextScene_ is a weak ref
}
I wonder if somehow the scene does not get deallocated properly. I verified that the cleanup method is being called however I also added a CCLOG call on the CCLayer dealloc method and rebuild the static library. The result is that the dealloc method doesn't seem to be called.
Is this normal? :D
I found that other people had similar issues. I am wondering if it has to do with retain cycles and self blocks. I really need to spend some time studying this unless, from EDIT 3, anyone can tell me already what I am doing wrong :-)
All memory capacity shared through all apps&processes run in iOS. So, other apps can use a lot of memory and your app receive memory warning too. You'll receive memory warnings until it is not enough.
To understand what actually happens with memory in your app you should
Profile your app with Leaks (ARC is not guarantee that you don't have leaks, i.e. self-capturing issue).
Use heapshot analysis (shortly described here http://bentrengrove.com/blog/2013/4/26/heapshot-analysis)
And checkout this post about memory & virtual memory in iOS: http://liam.flookes.com/wp/2012/05/03/finding-ios-memory/
I solved this by adding a print of the process effective memory usage in the console. In this way I could get a precise measurament of the real memory used by the App process. Using instrument proved to be imprecise as the real memory used did not match with the one shown on instruments.
This code can be used to get the effective memory usage:
-(vm_size_t)report_memory
{
struct task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(),
TASK_BASIC_INFO,
(task_info_t)&info,
&size);
if( kerr == KERN_SUCCESS ) {
} else {
NSLog(#"Error with task_info(): %s", mach_error_string(kerr));
}
return info.resident_size;
}
Is it is possible to write an app that uses 200MB, say? My iPad has 1GB, but I get
didReceiveMemoryWarning
after using 20 or 30MB and shortly after my app is killed. (I am the foreground app so I don't really see why I have to get this warning, why doesn't the OS close the background apps, but whatever). I am taking no action in didReceiveMemoryWarning (just logging it and calling super), is that why I am killed? Or is there other possible reasons?
So I understand I am supposed to free-up memory when I get the warning, but I don't want to! (Lets assume my app REALLY does need 200MB to operate).
If I did free-up some memory when I get the warning (how much?) then would my app then not be killed? And could I then carry on and use up MORE memory? If so I could create some "balloon" memory just so i can free it when warned and then at least my app survives. This seems insane though.
Or is it basically impossible to have an iPAD app that uses more than a few 10s of MB?
I recently had this problem. It basically comes down to the speed at which you allocate memory. If you try to grab a lot of memory up front then iOS will terminate you for using too much memory and not responding to memory warnings. iOS memory handling is ridiculous really. The worst thing is that my problems only arose AFTER I'd released the app on the app store. It took me ages to track down what the problem was :(
The way I managed to handle this was to allocate the RAM i needed at startup (64MB) slowly and hold off when I receive memory warnings. I create my own ViewController that displays an animated splash screen while I'm an initialising the memory usage In viewDidLoad I do the following (Meg is a simple inline function that multiplies by 1024* 1024):
AllocBlockSize = Meg( 2 );
mAllocBlock = (char*)malloc( mAllocBlockSize );
//[mpProgressLabel setText: #"Initialising Memory: 1MB"];
mpInitTimer = [NSTimer scheduledTimerWithTimeInterval: 0.5f target: self selector: #selector( AllocMemory ) userInfo: nil repeats: YES];
In my AllocMemory selector I do this:
- (void) AllocMemory
{
if ( self.view == nil )
return;
if ( mMemoryWarningCounter == 0 )
{
if ( mAllocBlockSize < Meg( 64 ) )
{
mAllocBlockSize *= 2;
mAllocBlock = (char*)realloc( mAllocBlock, mAllocBlockSize );
ZeroMemory( mAllocBlock, mAllocBlockSize );
if ( mAllocBlockSize == Meg( 64 ) )
{
mMemoryWarningCounter = 8;
}
}
else
{
free( mAllocBlock );
// Initialise main app here.
}
}
else
{
mMemoryWarningCounter--;
}
}
And to handle the memory warnings I do as follows:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
mMemoryWarningCounter += 4;
}
Also do note the ZeroMemory step. When I didn't have this here i would allocate 64MB and still get booted. I assume the touching the memory fully commits it to my app thus zeroing the memory was necessary to eliminate the memory warning and eviction problems I was suffering.
Something is not right. Any app can use about 1/3-1/2 the total physical ram on any device under most any version of iOS from 6-8 without being jetslammed (killed)
I can write a simple app that instantly takes 400mb on a 1gb device, and it's not killed - unless iOS can't terminate other services fast enough.
IOS 8 is more forgiving vs 6 or 7 as many of the launchdaemons now have a jetslammed priority flag which decides which order things get killed - as well as a memory limit so if a LD exceeds that high water mark, it's killed. IOS should let any app keep using memory until all the lower priority jetslammed services have been killed off. There's also another setting for LDs that terminate them when memory is under pressure - regardless of JS priority.
Once all that's left are services with a higher priority than a user app, that's when jetslamms/kills or should get memory warnings before it gets slammed
Programming on the iPad 6 (air 2) is MUCH easier. 2gb ram. 300-400MB free after boot and iOS will back down to using 700MB allowing 1.3GB for an app.... And I bet the iPhone 7 and mini 4s will have 2gb. That will let us see play station 3, or better games, for iOS IF AND ONLY IF users will pay the price of a normal PS3 game (20-80$). Most ppl will complain, but most spend more on these free to pay (play) apps with 4.99$-129.99$ iAps - absurd (Apple should limit iAps to 29.99)
Gone are the days of the 10% rule (where your app should use no more than 10% system ram)
Look at more hardcore, major iOS games.... They use 300-400MB on 1gb devices and won't run on 512mb devices.
So if you are being killed for 30MB something really is not right.
EDIT: guys, my question was about using the Instruments to see leaks, and the code as an example and side question, but you answered the side question and not the main problem.... Thank you for the answers, but I really need to find out how to work the Instruments with the simulator....
I am learning IOS development, In one of the codes I'm studying i think there is huge memory leak so I've tried learning how to use instruments. As i am learning right now, I am trying to use instruments with the simulator, but all the manuals i found are for connecting to a device and then using instruments and not with the simulator. every thing I've tried doesn't show any leaks in Instruments.
The app doesn't crash because i am guessing the memory leak is not that big, but when i am adding the following code it does crash, why is that, Even when i added the release every time, still crashes....what is wrong with the simulator? or with the code? working with xcode3, not 4.
for (int i = 0; i < 1000000; i++) {
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
NSLog(#"%#",testLeak);
[testLeak release];
}
And again, the app crashes and the simulator doesn't show any leaks, even when i put the "attach process" on "iPhone simulator".
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
The problem is that you are not actually allocating anything. NSString is internally smart enough to recognize that the above expression does not need to allocate anything because the constant string #"test1223" can neither mutate nor ever be deallocated. Thus, it just returns that string.
If you were to NSLog(#"%p", testLeak); you'd see the same address over and over.
Change the NSString to NSMutableString and you'll likely see the thousands of copies. Maybe; NSMutableString could be optimized to just point to the immutable copy until a mutation operation is performed (implementation detail). Or you could allocate an instance of some class of your own creation.
Keep in mind that Leaks doesn't necessarily show you all leaks; it can't because of the way it works.
For this kind of analysis, Heapshot analysis is very effective.
If it is crashing as described, please (a) post the crash log and (b) file a bug with your app (built for the simulator) attached to http://bugreport.apple.com/.
In general Instruments + Simulator will not be terribly useful; the simulator is only an approximation of what is running on the device.
[something release] doesn't actually free the memory the instant it is called - it just decreases the reference count of an object. If the count is 0, a [something dealloc] is called, and that frees the memory. I guess you are allocating memory faster than the system can free it... besides, doing 1.000.000 allocs in rapid succession instead of single huge one is probably as bad a coding practice as they come...
It may be that some stuff is getting autorelease'd, and that is using a ton of the heap. Try changing your code to this:
for (int i = 0; i < 1000000; i++) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
NSLog(#"%#",testLeak);
[testLeak release];
[pool drain];
}
Thank you all, I actualy Found the answer around 4 am yestorday...
when you want to test leaks on the emulator:
rum -> run with Performance Tool ->Leaks
If you select the simulator on the top right as the device to run the app on, It will lounch the simulator and the instruments and start the leak recorder, all in one click....
Have fun :-)
Erez