I often try to fix bugs that occur when I use my iphone for other memory hungry stuff and it needs to free some memory and thus unload some views from my app. I found this quite hard to simulate when I need it so I decided to make that will try to allocate as much memory as possible and force my tested app to release unused views etc.
I tried something simple as calling this every few hundred milliseconds but it for some reason didn't do anything
[[NSData alloc] initWithBytes:malloc(2048 * 1024) length:2048 * 1024];
Instruments show that apps is getting bigger and bigger, far beyond memory capacity of iphone (hundreds of mbs allocated) but I don't even get memory warning and it doesn't affect other apps at all. Is there some safeguard that somehow prevents iphone app form doing something like this? Or is there some mistake in my assumptions about how iphone works? How do you solve this problem when you face it?
EDIT: I am running my app on device, I wasn't able to get my view开发者_JS百科s unloaded on simulator even if I simulated memory warning (this sometimes work, but only rarely)
EDIT2: as bbum pointed out problem was indeed in virtual allocation, simple memset after allocation did the trick
void *data = malloc(1024 * 1024);
memset(data, 0, 1024 * 1024);
Most likely, what is happening is a tricksy bit of virtual addressing behind your back.
Namely, an application is free to reserve up to the full 4GB of 32-bit address space available to it (less than 4GB, really, because there is fragmentation caused by various system pieces).
When you do malloc(REALLYBIGNUMBER)
, the system uses vm_allocate()
to fulfill the request. The mach memory manager hands you back the addresses, but doesn't actually back it with real memory (kinda like the US economy has tons of $$$ in the market not backed by any real asset).
Physical memory will only be used when you write (or, technically, read) something to the memory. And it will only happen page by page.
So, if you were to walk through the allocated buffer in strides of 4096 (page size) and write one byte, you'd quickly see real memory being consumed.
Can you not just simulate a memory warning in the simulator using the 'Device' menu?
Here is what I do to simulate the use of a large block of memory.
#define MEGA (1024 * 1024)
- (void)stressTestTheApplicationBySimulatingMemoryScarcity {
NSUInteger sizeInMB = 20; // Size in MB. The higher, the more memory will be used here, leaving less for the application
// On iPad: 30 seems to be the upper limit you can ask. YMMV
#if MEMORY_STRESS_TEST_SCARCITY_ENABLED
#warning MEMORY_STRESS_TEST_SCARCITY_ENABLED - THIS SHOULD NOT HAPPEN IN PRODUCTION
#else
return;
#endif
NSLog(@"MEMORY STRESS TEST ENABLED (%dMB)", sizeInMB);
void * unusedMemoryBufferToSimulateMemoryScarcity = NSZoneCalloc(NSDefaultMallocZone(), sizeInMB * MEGA, 1);
if (NULL == unusedMemoryBufferToSimulateMemoryScarcity) {
NSLog(@"MEMORY STRESS TEST FAILED: Was unable to allocate requested memory");
return;
}
dataToRetainToSimulateMemoryScarcity = [[NSData dataWithBytesNoCopy:unusedMemoryBufferToSimulateMemoryScarcity
length:sizeInMB * MEGA
freeWhenDone:YES] retain];
if (dataToRetainToSimulateMemoryScarcity == nil) {
NSLog(@"Failed to retain data to simulate memory scarcity");
}
}
Done this way, the memory allocation is seen by Instruments.
The memset
solution suggested by bbum might have done the trick instead of the dataWithBytesNoCopy: length: freeWhenDone:
The warning and logs left in place help to make sure I do not ship it by error.
If you add some of this code to a project, I suggest you keep them as well (and turn on the Treat warning as error option)..
You need to bzero (or otherwise write to every page in) the memory blocks after you allocate them so that the virtual memory system creates all the pages in the block and marks them resident + dirty.
Are you running instruments on a process running on your device or the simulator?
I think in simulator it uses your Mac's memory which is obviously bigger than an iPhone's. Try running it on the device and see what happens?
Disco
精彩评论