I'm writing software that uses the security framework to get signing information from applications. I noticed that over time memory consumption goes up. After checking with Instruments, I saw an accumulation of objects coming from the internals of the security framework. These allocated objects don't seem to go away.
Following is a standalone code sample that seems to cause the problem in my env. Running it by itself creates a big number of objects that are not released. I also attached a screenshot from Instruments.
#include <Foundation/Foundation.h>
#include <Security/Security.h>
#include <unistd.h>
OSStatus get_signing_info(const char* path)
{
SecStaticCodeRef codeRef = NULL;
OSStatus status;
NSString* str = [NSString stringWithUTF8String:path];
NSURL* url = [NSURL fileURLWithPath:str];
status = SecStaticCodeCreateWithPath((__bridge CFURLRef)url, kSecCSDefaultFlags, &codeRef);
CFDictionaryRef _info = NULL;
status = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &_info);
NSDictionary* info = (__bridge_transfer NSDictionary *)_info;
int flags = [[info objectForKey:(NSString *)kSecCodeInfoFlags] intValue];
NSLog(@"%d", flags);
CFRelease(codeRef);
return status;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
for (int i = 0; i < 1000; ++i) {
@autoreleasepool {
OSStatus status = get_signing_info(argv[1]);
NSLog(@"i=%d, status=%d", i, status);
}
}
sleep(100);
}
return 0;
}
Is there a way to get rid of these objects that clog up the memory? Or perhaps use the framework differently to avoid this issue?
Following is a standalone code sample that seems to cause the problem in my env. Running it by itself creates a big number of objects that are not released. I also attached a screenshot from Instruments.
I got your code snippet running and there are a few things you should look at first.
-
If you invert the call tree, you'll find that the single largest allocation is a 256kb call to "mach_vm_map" which actually came from "_os_trace_init_slow". That's a "leak" in the sense that the system isn't going to delete it, but that's because it's a tied to a one time initialization that's intentionally never freed.
-
The second largest set is harder to follow but appears to be deep in the internals of the unicode runtime and looks like a similar one-time initialization "charge".
-
Most importantly, I'm not seeing any actual memory growth. Are were my top level results:
1000 Reps:
607.05 KB 95.7% 3014 0x18d713153
607.05 KB 95.7% 3014 main
19.62 KB 3.0% 123 0x18d712ef3
7.23 KB 1.1% 26 start_wqthread
5000 Reps:
607.05 KB 96.6% 3014 0x18d713153
607.05 KB 96.6% 3014 main
19.62 KB 3.1% 123 0x18d712ef3
1.61 KB 0.2% 21 start_wqthread
10000 Reps:
607.00 KB 95.7% 3013 0x18d713153
19.62 KB 3.0% 123 0x18d712ef3
7.19 KB 1.1% 24 start_wqthread
- It's hard to audit every allocation, but there is every indication that all of these are one time initialization of very low level sub-systems (like os_trace and unicode). These subsystems initialize this way because they're so widely used within the entire system that freeing them at all would cause far more performance loss than it would gain.
__
Kevin Elliott
DTS Engineer, CoreOS/Hardware