Somehow I am seeing memory increase in my simulator (the running bar graph).
I have an object called MyObject which has nothing but an int. In some other class (say MyCar in MyCar.h) I have an array of MyObjects, e.g. MyObject* junk[8];
and a method to return a specific one by index:
-(MyObject*) getJunk:(int) index{
if(junk[index] == nil){
return nil;
}
return junk[index];
}
In another class called DataModel I have a MyCar *mycar;
in DataModel I call a method ask
-(void) ask{
if(mycar == nil){
return nil;
}
for(int i=0;i<8;i++){
if( [mycar getJunk:i] == nil){
continue;
}
}
}
I call [self ask] say 10,000 times each time. I am using ARC, but I notice each time the iOS memory bar goes up. So something is accumulating memory; I don't know what, I don't have any allocs. Yes, this happens even if I write such non useless code which does nothing with the junk[i] returned to data model.
I even tried
-(void) ask{
if(mycar == nil){
return nil;
}
for(int i=0;i<8;i++){
MyObject* temp_junk = [mycar getJunk:i];
if( temp_junk == nil){
continue;
}
temp_junk = nil;
}
}
If you are calling [self ask] 10000 times in a loop, like this:
for (int = 0; i < 10000; i++)
[self ask];
then you are accumulating autoreleased objects inside the loop - those objects will not be released until your loop ends and the main run loop flushes the autorelease pool. Try creating your own autorelease pool inside your loop, like this:
for (int i = 0 ; i < 10000; i++)
{
#autoreleasepool
{
[self ask];
}
}
That will create a new pool each time through the loop and flush it each time so those autoreleased objects will go away.
Related
I have an image processing application that I am using to test potential speedup of blocks/threads on iOS. The algorithm works fine, uses a fair amount of memory and then dumps it after running.
I made a test suite that runs the algorithm 10 times sequentially and 10 times using my parallel implementation. If I try to run it on a phone it crashes due to memory pressure - it ends up needing about 432MB of memory. However, once the suite is done it all finally gets cleaned up :/
Each individual run is using around 25MB of memory. So I thought the solution would be to reset all of my objects after each run and they would get cleaned up. I basically have 2 processing objects that do all of my work. So after each run I thought setting them to nil would cause them to be recreated and the old versions to be destroyed and that memory freed. However, it had no effect on my memory usage.
Is there something more I need to do to free up the memory in-between calls? I thought Objective-C was now using reference counting, and once I eliminate the only reference - in my viewController - that it would be freed. Any suggestions would be greatly appreciated.
This is the test suite algorithm with my attempted memory-freeing:
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
[self printResults];
[self clearResults];
}
Other than working on the algorithm to improve memory usage you could give #autoreleasepool a shot. This will free your freeable objects used in between every loop, without the need of the current loop cycle to end.
for (int i = 0; i < 10; i++) {
#autoreleasepool {
}
}
From the documentation:
In many situations, allowing temporary objects to accumulate until the
end of the current event-loop iteration does not result in excessive
overhead; in some situations, however, you may create a large number
of temporary objects that add substantially to memory footprint and
that you want to dispose of more quickly. In these latter cases, you
can create your own autorelease pool block. At the end of the block,
the temporary objects are released, which typically results in their
deallocation thereby reducing the program’s memory footprint
Some source code would help. In absence of that, a general suggestion: wrap the code that is doing the image process in an autoreleasepool (see this Apple document).
This will discard temporary objects as soon as possible, reducing memory spike.
Autorelease pool should be used here
Many programs create temporary objects that are autoreleased. These
objects add to the program’s memory footprint until the end of the
block. In many situations, allowing temporary objects to accumulate
until the end of the current event-loop iteration does not result in
excessive overhead; in some situations, however, you may create a
large number of temporary objects that add substantially to memory
footprint and that you want to dispose of more quickly. In these
latter cases, you can create your own autorelease pool block. At the
end of the block, the temporary objects are released, which typically
results in their deallocation thereby reducing the program’s memory
footprint
- (void)runTestSuite
{
// Sequential
NSLog(#"Sequential Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:NO];
}
}
[self printResults];
[self clearResults];
// Parallel
NSLog(#"Parallel Run: ");
for (int i = 0; i < 10; i++) {
#autoreleasepool
{
self.imageView.image = self.startImage;
self.imageManipulator = nil;
self.objectRecognizer = nil;
for (UIView *view in self.gameView.subviews) {
[view removeFromSuperview];
}
[self processImageInParallel:YES];
}
}
[self printResults];
[self clearResults];
}
I've probably been starting at this too long and simply can't see the logic problem. I'm changing background images on a pan gesture. Swiping left/right cycles thru an array of image names for the background and loops.
Swiping Right (increasing) works fine, it loops back to the start of my array.
Swiping Left (decreasing stops at the first object in my array (objectIndex: 0).
NSLog(#"_imageBackgroundIndex Before:%d",_imageBackgroundIndex);
if ([_panDirection isEqual:#"Right"])
{
_imageBackgroundIndex = _imageBackgroundIndex + 1;
}
if ([_panDirection isEqual:#"Left"])
{
_imageBackgroundIndex = _imageBackgroundIndex - 1;
}
NSLog(#"_imageBackgroundIndex After:%d",_imageBackgroundIndex);
if (_imageBackgroundIndex > ([_backgroundImages count] - 1))
{
_imageBackgroundIndex = 0;
}
if (_imageBackgroundIndex < 0)
{
_imageBackgroundIndex = ([_backgroundImages count] - 1);
}
[[self childNodeWithName:kBackgroundName] runAction:
[SKAction setTexture:
[SKTexture textureWithImageNamed:
[_backgroundImages objectAtIndex:_imageBackgroundIndex]]]];
Anyone see the issue?
Code looks okay (though there are some style improvements to consider once you get it working). I'd wager that you have _imageBackgroundIndex declared as an unsigned int or NSUInteger. When you think it drops below zero, it's really taking on a huge unsigned value.
This code will work (and has better style) even if the counter is declared unsigned...
- (void)incrementIndex {
BOOL increment = self.imageBackgroundIndex < self.backgroundImages.count-1;
self.imageBackgroundIndex = (increment)? self.imageBackgroundIndex+1 : 0;
}
- (void)decrementIndex {
BOOL decrement = self.imageBackgroundIndex > 0;
self.imageBackgroundIndex = (decrement)? self.imageBackgroundIndex-1 : self.backgroundImages.count-1;
}
Then your pan method becomes simpler:
// not sure how panDirection is initialized, is it really a string #"Left" or #"Right"?
// if so, use isEqualToString to compare strings
if ([_panDirection isEqualToString:#"Right"]) [self incrementIndex];
else if ([_panDirection isEqualToString:#"Left"]) [self decrementIndex];
[[self childNodeWithName:kBackgroundName] runAction:// ... etc
I need to do this : alloc objects until memory warning is called and then release all objects. But I have some problems. How can I do this? I need code example because the problem is that : the code. I have a class that doesn't use ARC. This class has a method which alloc N objects that are saved into an array. I need the memory is filled until didReceiveMemoryWarning is called because this is the only method to "free" RAM memory on iOS. Then, I will release all. I think the cleaner memory apps for the iPhone on the App Store use this trick to "free" the memory.
Thanks in advance
You'll have to fill in the missing details but here is what I have used before. Credit goes to who/where ever I found it. This will work on ARC and non ARC projects. I have found that usually you get 2-3 warnings before you're completely dead. Good luck. Dinner length is how much of a chunk that gets allocated each time. if you want more fine grained memory control change the size.
-(IBAction)startEatingMemory:(id)sender
{
if(self.belly == nil){
self.belly = [NSMutableArray array];
}
self.paused = false;
[self eatMemory];
}
- (IBAction)pauseEat:(id)sender {
self.paused = true;
[[self class]cancelPreviousPerformRequestsWithTarget:self selector:#selector(eatMemory) object:nil];
}
- (IBAction)stopEatingMemory:(id)sender {
[self pauseEat:self];
[self.belly removeAllObjects];
[[self class] cancelPreviousPerformRequestsWithTarget:self selector:#selector(eatMemory) object:nil];
}
-(void)eatMemory
{
unsigned long dinnerLength = 1024 * 1024;
char *dinner = malloc(sizeof(char) * dinnerLength);
for (int i=0; i < dinnerLength; i++)
{
//write to each byte ensure that the memory pages are actually allocated
dinner[i] = '0';
}
NSData *plate = [NSData dataWithBytesNoCopy:dinner length:dinnerLength freeWhenDone:YES];
[self.belly addObject:plate];
[self performSelector:#selector(eatMemory) withObject:nil afterDelay:.1];
}
-(void)didReceiveMemoryWarning
{
[self pauseEat:self];
<#Could release all here#>
[super didReceiveMemoryWarning];
}
I would edit/subclass the class that doesn't use ARC to either use ARC, or add a method to it that releases the N objects.
I want to synchronize some data with a web service. For each item I have to make a asynchronous call.
I want to have a completion block witch is called, when each item was synchronized. For each item I am able to perform a completion block. Now, I don't know a good way how to do it.
This is the interface:
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
for (int i = 0, n = [items count]; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
// What do do here?
}];
}
// And/or here?
}
-(void) synchronizeItemOnComplete:(CompleteBlock) block {
// do something
block();
}
How can I wait for the synchronization and then perform the block?
I tried something like this:
NSArray* items = // get items
__block int countOfItemsUntilDone = [items count];
for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
[self synchronizeItem:[items objectAtIndex:i] onComplete:^{
countOfItemsUntilDone--;
}];
}
dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
while (countOfItemsUntilDone > 0) {
usleep(1000); // wait a little bit
}
block();
});
dispatch_release(queue);
But I think this is a quite bad way. Any ideas?
Instead of spinning in a loop waiting for the counter to equal zero, check the counter value each time you decrement it, then fire an event when it reaches zero.
-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
NSArray* items = // get items
__block NSUInteger remaining = [items count];
for (ItemClass* item in items) {
[self synchronizeItemImage:item onComplete:^{
--remaining;
if (remaining == 0) {
block();
}
}];
}
}
To explain why it feels wrong, there are two things you're doing here that you should do either never or rarely:
Using background queues. This is difficult and bug-prone. Don't do it without reading up a lot about writing concurrent code. You also only really need to do this if an operation blocks for a substantial amount of time (eg., to read a file from disk, or perform an intensive calculation). Don't assume you need to do it unless you have a good reason (eg., a measurable performance problem).
Spinning in a loop, checking a variable for changes and calling sleep. You should never do this.
Also, if you're looping over the elements in an array, the for ... in syntax is much nicer (and potentially more efficient) calling objectAtIndex: on each index.
Never check or decrement shared memory in different threads like this, it can cause races. Use a dispatch group to do what you're doing.
dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();
for (ItemClass *item in items) {
dispatch_group_async(itemsGroup, myBGQueue, ^{
[self synchronizeItemImage:item];
});
}
/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);
dispatch_release(itemsGroup);
You can use these to use synchronously.
GCD and this
performSelector:waitUntilDone:YES
I've edited the previous post with some working code for convenience.
The follow code (ARC'ed) seems to leak and will crash the sim or the device after running for a short period of time:
#define kROWS 100
#define kCols 34
void run();
static ViewController *instance;
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *nsBackColor;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.nsBackColor = [NSMutableArray arrayWithCapacity:1];
instance = self;
// set up a '2D array'
for (int x = 0; x < kROWS; x++) {
[self.nsBackColor addObject:[NSMutableArray arrayWithCapacity:1]];
for (int y = 0; y < kCols; y++) {
[[self.nsBackColor objectAtIndex:x] addObject:[UIColor whiteColor]];
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
run();
});
}
- (void)plotColor:(UIColor *)color atX:(short)x andY:(short)y {
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:color];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
void plot(short xLoc, short yLoc,
short backRed, short backGreen, short backBlue) {
#autoreleasepool {
[instance plotColor:[UIColor colorWithRed:((float)backRed/100)
green:((float)backGreen/100)
blue:((float)backBlue/100)
alpha:(float)1]
atX:xLoc andY:yLoc];
}
}
void run() {
short x = 0;
short y = 0;
short backRed = 0;
short backGreen = 0;
short backBlue = 0;
while (true) {
x++;
if (x >= kROWS) {
x = 0;
}
y++;
if (y >= kCols) {
y = 0;
}
backRed = arc4random() % 255;
backBlue = arc4random() % 255;
backGreen = arc4random() % 255;
plot(x, y, backRed, backGreen, backBlue);
usleep(1000);
}
}
If I let this run on the device or the simulator long enough (a couple minutes) I'll receive either an mmap malloc error (sim) or a memory warning (device) and crash.
Going through Instruments Allocations I can see +[UIColor colorWithRed:green:blue:alpha:] start to ballon until finally hitting the memory wall.
I can assign the UIColor to a property (directly or by doing a copy), say self.myColor = color, and there is no such leak.
I can do this too:
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:[self description]];
and I get the same leak.
It seems to me that object replaced in the array (and yes, this did originally start as a 2D c array but I thought that was the issue) is forever lost and leaked and not properly released.
This would be the Instruments->Allocations after running for a short period of time:
Any help would be much appreciated and more information can be provided.
What is happening is that you are creating an autorelease pool at every iteration.
So the following line runs every time with a new autorelease pool:
[[self.nsBackColor objectAtIndex:x] replaceObjectAtIndex:y withObject:color];
Therefore color increases its reference count within the local pool by one, while [[self.nsBackColor objectAtIndex:x] objectAtIndex:y] decreases its reference count within the local pool by one. But here's the catch: That item was color in the previous iteration, which had its reference count managed by the previous pool that you drained/released earlier.
So what should have happened is as the previous pool was released in the previous iteration, that object was released. Its reference count was 2 (one for [UIColor colorWith...], one for adding to the array), so it should have received 2 release messages as soon as the pool was drained, and the pointer in [[self.nsBackColor objectAtIndex:x] objectAtIndex:y] should be left dangling until you replace it in the current iteration with a pointer to color.
Clearly that's not happening exactly the way it's supposed to be, or the way I understand it. However, the #autoreleasepool {} directive is clearly misplaced. It should be around the while (true) loop, or removed altogether in favor of the thread's pool.