Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@gsong
Created March 17, 2012 18:02
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save gsong/2063616 to your computer and use it in GitHub Desktop.
Save gsong/2063616 to your computer and use it in GitHub Desktop.
LaunchAgent to release inactive memory on OS X 10.7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>net.damacy.releasemem</string>
<key>ProgramArguments</key>
<array>
<string>/Users/george/.bin/releasemem.py</string>
</array>
<key>StartInterval</key>
<integer>60</integer>
<key>Nice</key>
<integer>20</integer>
<key>LowPriorityIO</key>
<true/>
</dict>
</plist>
#!/usr/bin/env python
import os
import re
import sys
from subprocess import call, Popen, PIPE
INACTIVE_THRESHOLD = 1024 # Number of MBs
FREE_THRESHOLD = INACTIVE_THRESHOLD / 2
RE_INACTIVE = re.compile('Pages inactive:\s+(\d+)')
RE_FREE = re.compile('Pages free:\s+(\d+)')
RE_SPECULATIVE = re.compile('Pages speculative:\s+(\d+)')
LOCK_FILE = '/var/tmp/releasemem.lock'
def acquire_lock():
try:
os.open(LOCK_FILE, os.O_CREAT | os.O_EXLOCK | os.O_NDELAY)
except OSError:
sys.exit('Could not acquire lock.')
def pages2mb(page_count):
return int(page_count) * 4096 / 1024 ** 2
def free_inactive():
vmstat = Popen('vm_stat', shell=True, stdout=PIPE).stdout.read()
inactive = pages2mb(RE_INACTIVE.search(vmstat).group(1))
free = pages2mb(RE_FREE.search(vmstat).group(1)) + \
pages2mb(RE_SPECULATIVE.search(vmstat).group(1))
return free, inactive
def main():
acquire_lock()
free, inactive = free_inactive()
if (free < FREE_THRESHOLD) and (inactive > INACTIVE_THRESHOLD):
print("Free: %dmb < %dmb" % (free, FREE_THRESHOLD))
print("Inactive: %dmb > %dmb" % (inactive, INACTIVE_THRESHOLD))
print('Purging...')
call('/usr/bin/purge', shell=True)
if __name__ == '__main__':
main()
@gsong
Copy link
Author

gsong commented Mar 17, 2012

  1. Make sure releasemem.py is executable: chmod +x.
  2. Adjust the inactive and free memory thresholds on lines 9 and 10 of releasemem.py. The setting here is for an 8GB machine.
  3. Modify the path of releasemem.py accordingly in line 9 of net.damacy.releasemem.plist.
  4. Adjust the frequency for the job by adjusting line 12 of net.damacy.releasemem.plist. Here it's set for every 60 seconds.
  5. Save or symlink net.damacy.releasemem.plist to $HOME/Library/LaunchAgents.
  6. launchctl load -w $HOME/Library/LaunchAgents/net.damacy.releasemem.plist.

@Firestorm-Graphics
Copy link

maybe a more user friendly tut would be an idea, for instance releasemem.py where do we put it? your path points to a .bin yet with files unhidden i see no bin file other than in root, does its location matter?

@gsong
Copy link
Author

gsong commented Feb 15, 2013

There's a blog post to go with this gist.

You can put releasemem.py anywhere, as long as you point to the correct location in the launchd plist. In my case, I created .bin in my home directory.

@satishv-denali
Copy link

Thanks buddy. This script seems to be working just fine on my Mac Pro with Lion and 8GB :) Appreciate the effort.

@roy-so
Copy link

roy-so commented Jun 23, 2013

Need your help. I'm using Mountain Lion but with no luck, also have made the releasemem.py to be executable. But I saw the message in the console "failed to exec(3) for weird reason: 13". Any advice?

------------ Updates ------------

Changed "#!/usr/bin/env python" to "#!/usr/bin/python". It works! Nice code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment