Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old Feb 20, 2012, 03:56 PM   #1
thundersteele
macrumors 68030
 
Join Date: Oct 2011
memory leak, IOkit, and ARC

I'm working on a small app that provides information similar to coconut battery, but plugs into the menu bar. Not exactly groundbreaking, it's more a hobby for me right now

The app relies on IOKit to access the battery information.

After launching the app, it uses about 10 MB of memory. However I noticed that after about a week of running, the memory usage is up to about 100 MB, and keeps growing at a very slow rate. Steps taken:

Enabled ARC (automatic reference counting) and adjusted the code accordingly. No change.
Used Instruments and found that IOkit leaked a few kb per minute - the app checks the battery status every 10 seconds, and each time it would leak some memory.
Disabled ARC, enabled garbage collection. This apparently closed most leaks, the app is down to leaking 20 KB per hour. At this point a small leakage occurs every few minutes, and doesn't seem to be correlated with any particular action inside the program.

At this point I should probably stop worrying... a full month of running would make the app use 20 MB. For comparison, I ran coconut battery through instruments, and it turns out to leak about the same amount of memory.

Below is a screenshot of instruments - it seems to indicate that the leak is somewhere deep within IOKit. However I'm well aware in most cases it's still a user error.
Also below is the relevant snipplet of the method that is called every 10 seconds:

Code:
 - (void) updateBatteryInfo
{
    // go to battery entry!    
    ioRegistryRoot = 
        IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/AppleACPIPlatformExpert/SMB0/AppleECSMBusController/AppleSmartBatteryManager/AppleSmartBattery");
    
    // read out properties
    tester=IORegistryEntryCreateCFProperties(ioRegistryRoot, &properties, kCFAllocatorDefault,0);
    
    // set above values!
    CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(properties, CFSTR("CycleCount")), 9, &CycleCount);
    CFNumberGetValue((CFNumberRef) CFDictionaryGetValue(properties, CFSTR("CurrentCapacity")), 9, &CurrentCharge);

    // more of the same commands

}
I should probably try to manually release stuff (when implementing ARC, you have to remove all retain/release cycles).
Attached Thumbnails
Click image for larger version

Name:	Screen Shot 2012-02-20 at 3.29.32 PM.png
Views:	38
Size:	152.6 KB
ID:	325460  
thundersteele is offline   0 Reply With Quote
Old Feb 20, 2012, 04:55 PM   #2
subsonix
macrumors 68030
 
Join Date: Feb 2008
Do you ever release that dictionary?
subsonix is offline   0 Reply With Quote
Old Feb 20, 2012, 05:27 PM   #3
thundersteele
Thread Starter
macrumors 68030
 
Join Date: Oct 2011
Quote:
Originally Posted by subsonix View Post
Do you ever release that dictionary?
No. I was following the ARC guide in the OSX developer documentation, which tells you to remove all release commands. Of course then I stopped using ARC, so maybe I should put back manual releases.

The way things work is that I have a class whose properties are the battery information as well as the (mutable) dictionary "properties" and the io_registry_entry_t "ioRegistryRoot."

Upon program launch, I create an instance of the class. The update method then updates the contents of the dictionary, and updates the values of the battery charge etc. In this sense, the program always uses the same dictionary.
thundersteele is offline   0 Reply With Quote
Old Feb 20, 2012, 05:32 PM   #4
subsonix
macrumors 68030
 
Join Date: Feb 2008
Quote:
Originally Posted by thundersteele View Post
No. I was following the ARC guide in the OSX developer documentation, which tells you to remove all release commands. Of course then I stopped using ARC, so maybe I should put back manual releases.

The way things work is that I have a class whose properties are the battery information as well as the (mutable) dictionary "properties" and the io_registry_entry_t "ioRegistryRoot."

Upon program launch, I create an instance of the class. The update method then updates the contents of the dictionary, and updates the values of the battery charge etc. In this sense, the program always uses the same dictionary.
ARC relates to Cocoa, not CoreFoundation which is a C api. The "Create" rule should apply, ie function names which has the word "create" in them creates objects that you own, and you must release with CFRelease.

https://developer.apple.com/library/...Ownership.html

Edit:

Quote:
Originally Posted by thundersteele View Post
Upon program launch, I create an instance of the class. The update method then updates the contents of the dictionary, and updates the values of the battery charge etc. In this sense, the program always uses the same dictionary.
The class is something different, because inside the class you have methods like your updateBatteryInfo, each time you call this method you create a new CFDictionary object, if your reference to this object is an instance variable in your class then the value of that variable will contain a new address for each call to updateBatteryInfo, ie you will lose reference to the last one.

Last edited by subsonix; Feb 20, 2012 at 05:42 PM.
subsonix is offline   0 Reply With Quote
Old Feb 20, 2012, 05:48 PM   #5
thundersteele
Thread Starter
macrumors 68030
 
Join Date: Oct 2011
Quote:
Originally Posted by subsonix View Post
ARC relates to Cocoa, not CoreFoundation which is a C api. The "Create" rule should apply, ie function names which has the word "create" in them creates objects that you own, and you must release with CFRelease.

https://developer.apple.com/library/...Ownership.html

Edit:



The class is something different, because inside the class you have methods like your updateBatteryInfo, each time you call this method you create a new CFDictionary object, if your reference to this object is an instance variable in your class then the value of that variable will contain a new address for each call to updateBatteryInfo, ie you will lose reference to the last one.
Thanks for the explanations and for the link. This gives me something to do for the weekend!
I was not aware that ARC is restricted to Cocoa, although upon thinking about it, it's hardly a surprise.
thundersteele is offline   0 Reply With Quote
Old Feb 20, 2012, 08:31 PM   #6
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
According to Apple's documentation, CFDictionary is "toll-free bridged" to NSDictionary: if you declare tester as a NSDictionary, you may be able to simply cast the call that creates the dictionary and let ARC manage the object for you. If tester is a strong ivar, ARC will release the previous dictionary before assigning the new value.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old Feb 20, 2012, 08:55 PM   #7
subsonix
macrumors 68030
 
Join Date: Feb 2008
Quote:
Originally Posted by Sydde View Post
According to Apple's documentation, CFDictionary is "toll-free bridged" to NSDictionary: if you declare tester as a NSDictionary, you may be able to simply cast the call that creates the dictionary and let ARC manage the object for you. If tester is a strong ivar, ARC will release the previous dictionary before assigning the new value.
But tester is not a CFDictionary, it's a kern_return_t type that is part of IOKit which is embedded C++. The CFDictionary is the pointer called "properties", as you can see it's address is passed as an argument which means that it's modified by IORegistryEntryCreateCFProperties. This make the whole casting idea non-obvious IMO, although __bridge_transfer seems to be the ticket.

Also related to IOKit, there might of course be more memory that needs to be taken care of that is not shown here.

I'm assuming that the procedure that happens here is something like this: the method to get information from IOKit is called periodically, after which the GUI is updated (I suppose all objects could be release here), rinse repeat.
subsonix is offline   0 Reply With Quote
Old Feb 21, 2012, 12:49 AM   #8
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
Quote:
Originally Posted by subsonix View Post
Also related to IOKit, there might of course be more memory that needs to be taken care of that is not shown here.
There appears to be a function IOObjectRelease() that might be called for WRT the ioRegistryRoot variable at some appropriate point. Perhaps it could safely be disposed of right after the dictionary is created?
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old Feb 21, 2012, 11:52 AM   #9
subsonix
macrumors 68030
 
Join Date: Feb 2008
Quote:
Originally Posted by Sydde View Post
There appears to be a function IOObjectRelease() that might be called for WRT the ioRegistryRoot variable at some appropriate point. Perhaps it could safely be disposed of right after the dictionary is created?
I don't know, it might even be safe to call this function once at program startup then keep the reference for this object for the lifetime of the app since this relates to battery info. Might be wrong about that though, but the release of this object should by done by the caller it seems.
subsonix is offline   0 Reply With Quote
Old Feb 22, 2012, 02:30 PM   #10
thundersteele
Thread Starter
macrumors 68030
 
Join Date: Oct 2011
Quote:
Originally Posted by subsonix View Post
ARC relates to Cocoa, not CoreFoundation which is a C api. The "Create" rule should apply, ie function names which has the word "create" in them creates objects that you own, and you must release with CFRelease.

The class is something different, because inside the class you have methods like your updateBatteryInfo, each time you call this method you create a new CFDictionary object, if your reference to this object is an instance variable in your class then the value of that variable will contain a new address for each call to updateBatteryInfo, ie you will lose reference to the last one.
This was exactly the reason I was leaking memory. I added a simple CFRelease(properties) in the update function, before a new dictionary is created.
ARC takes care of the remaining retain/release cycles, so that I have zero leaks now without garbage collection.

Thanks a lot!!!
thundersteele is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Similar Threads
thread Thread Starter Forum Replies Last Post
Strange memory problems or memory leak in OS??? OldGuyTom OS X 10.8 Mountain Lion 12 Oct 11, 2013 01:47 PM
iPhone: Help with Memory Leak Stixx31 Jailbreaks and iOS Hacks 9 May 3, 2013 02:56 AM
Resolved: Memory never released in ARC project? xArtx iPhone/iPad Programming 4 Feb 7, 2013 12:49 AM
Memory leak spilakalb iPhone/iPad Programming 5 Oct 4, 2012 07:15 AM
What's a memory leak 3bs Alternatives to iOS and iOS Devices 10 Sep 16, 2012 05:23 PM

Forum Jump

All times are GMT -5. The time now is 09:22 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC