1

In the interest of knowing how stuff works I have written a simple iPhone application that has a start button. Pressing that button triggers an action to do the following:

- (IBAction)start:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @autoreleasepool {
        NSString *urlString = @"http://www.aftonbladet.se";
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
        NSHTTPURLResponse *response = nil;
        NSError *error = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        if (error) {
            NSLog(@"Error: %@", [error localizedDescription]);
        }
        if (data) {
            NSLog(@"Data length: %d", [data length]);
        }
        if (response) {
            NSLog(@"Status code: %d", [(NSHTTPURLResponse*)response statusCode]);
        }
        [ViewController ReportMemory];
      }
});

}

The ReportMemory function looks like this:

+ (void)ReportMemory {
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 ) {
    NSLog(@"Memory in use: %u kB", info.resident_size/1024);
} else {
    NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
}

}

I've read that global queues have autorelease pools but they are only emptied intermittently, so I have tried both with and without the @autoreleasepool macro and I cannot see any difference with regards to memory usage.

The question is, why does ReportMemory show more and more memory used for each time I press the start button? I would have thought that the auto release pools at some point would be emptied. But in my case it keeps adding up until I get a few memory warnings, and when ReportMemory reports about 400MB used, the application gets shut down.

Please note, the use of sendSynchronousRequest like this is for demonstration purposes only.

JugsteR
  • 1,144
  • 8
  • 22
  • Try moving your call to `[ViewController ReportMemory];` so that it is *outside* the `@autoreleasepool` you set up. – Nicholas Hart Aug 21 '13 at 23:25
  • That makes no difference with regards to memory usage, it still grows for each press of the button. It does however print memory usage before the actual request is done. – JugsteR Aug 22 '13 at 06:28
  • Have you tried looking at it with the Allocations template in Instruments? Specifically, run your program, click the button once (to prewarm any OS caches), then do a heapshot, then click it again and do a second heapshot (the actual test run) and then wait a bit and see what was allocated but not released during the test interval. – ipmcc Aug 22 '13 at 13:41
  • I have tried it in instruments and I get a similar pattern, but the ReportMemory method never seems to report less memory even when objects have been released. And when Instruments show very little memory usage (less than 10MB), I still get the three memory warnings and then the app closes. The process that eventually closes down my app seems to work more on the numbers given by ReportMemory than on what Instruments is showing. Anyway, my questions is why the ReportMemory always grows. Should it never be less than previous requests if something had been released? – JugsteR Aug 22 '13 at 18:54

1 Answers1

1

I ran the code you posted in the Allocations Instrument and the only appreciable heap growth I'm seeing from a single, marginal invocation of this method is coming from HTTP cookie storage. Banging on it for a minute or so, the heap allocations looked like this:

Instruments Heap Trace

In short, yes it grows, but every once in a while it releases a bunch of accumulated resources. I suspect there's something else going on that's not captured by this code. You should run your (whole) app in Instruments and see what's going on.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • Did you actually see the memory used as reported by ReportMemory decrease? I don't. And as you suspect the actual application I have this proboem on is somewhat more advanced, but this is the simplest example I can make that exhibits the same behaviour. In the more advanced application I've made ReportMemory hovers at about 30M and stays there. That is when I have removed the piece of code that has this behaviour. – JugsteR Aug 22 '13 at 18:57
  • 1
    Instruments is going to be a *much* better indicator of effective memory use than `task_info_t.resident_size`. I reiterate my suggestion to use it. Specifically, it will tell you the call stacks of all the allocations, both heap and VM pages (excepting pages mapped into your process by other processes), that are contributing to your growing memory footprint. – ipmcc Aug 22 '13 at 19:10
  • I realize that, and I agree to some extent. But that is not my question. This questions is about ReportMemory, why it never decreases. Also the manner in which applications are shut down seem to coincide more with what ReportMemory says than what Instruments says. When ReportMemory says 400MB allocated, the application gets shut down. At this point Instruments is showing less than 10MB allocated with no leaks. I can hardly make that a point to users of my app: "Ok it got shut down because of memory pressure, but Instruments is only showing 10MB." – JugsteR Aug 22 '13 at 19:49
  • I'm now trying it a tight loop with your ReportMemory function, and yes, I am seeing it go down. For me, the value reported by ReportMemory seems to sawtooth between about 68MB and 75MB - definitely not seeing unbounded growth. – ipmcc Aug 22 '13 at 20:02
  • ...behavior was consistent over 5 minutes of running -- over 1300 requests, no unbounded growth observed either in Instruments or with ReportMemory. – ipmcc Aug 22 '13 at 20:09
  • You are right! This example does not demonstrate the issue I am seeing in my application. Indeed ReportMemory levels out at ~95MB in the simulator. Since my application levels out at 30MB without the code that exhibits this issue, I thought that when I had passed 70MB that it was unbounded. However, simulator and device probably have different memory levels before they conserve memory etc. I'll have to examine the faulty code a little bit more to see if I can truly make a simpler example that displays the unbounded growth, while Instruments still show very little memory usage. – JugsteR Aug 23 '13 at 08:39