In a recent post about using CrashPlan for backup but getting the java process to behave, I said to create a file named kill_java_mem.sh in ~/bin/cronjobs & put the following in it:

#!/bin/bash

# Figure out how much RAM Java is using
# Uses the RSS column of ps (#6)
# RSS = the real memory (resident set) size of the process (in 1024 byte units)
javamem="$(ps aux | grep java | grep root | awk -F' ' '{print $6}')"

# Get Java’s PID (2nd column of ps)
javapid="$(ps aux | grep java | grep root | awk -F' ' '{print $2}')"

# If Java is using over 200 MB of RAM, kill the process
if [ $javamem gt 204800 ]
then
  kill $javapid
fi

After thinking about it, I decided that this script was seriously flawed due to a couple of stupid things I overlooked.

First, what if there’s more than one java process? My script just assumes there’s one. Actually, I only care about one at this point, since I only have one app on my Mac that uses Java: CrashPlan. Even so, the script should be rewritten to reflect this.

Second, CrashPlan is configured to run from 5 p.m. to 9 a.m., which is fine. While I’m awake from 5 p.m. to about 1 a.m., it’s OK for CrashPlan to run, but I don’t want the java process to use more than 200 MB of RAM during that time. After 1 a.m. & until I wake up, it’s OK for Java to use more than 200 MB of RAM while CrashPlan backs things up overnight.

In other words, here’s the breakdown of what I want by time:

  • 9 a.m.–5 p.m.: CrashPlan shouldn’t run, & java processes should not use more than 200 MB of RAM
  • 5 p.m.–1 a.m.: CrashPlan should run, but java processes should not use more than 200 MB of RAM
  • 1 a.m.–9 a.m.: CrashPlan should run, & java processes can use more than 200 MB of RAM

How to do that with my script? If I was using cron to manage when my script ran, I can just tell cron when to run the script & it would be over & done with. Unfortunately, while cron is still present in Mac OS X, it’s deprecated, & on top of that, while on my Mac I try to do things the Mac way. That means launchd, & launchd doesn’t allow you specify a range of time during which things should run. That means you have to set the time span in your script.1

Here’s the newly written script that takes the two issues into account. Notice that I’m now looking specifically for the CrashPlan PID & I’m only killing the java process during a specific time span:

#!/bin/bash

# Figure out how much RAM Java is using
# Uses the RSS column of ps (#6)
# RSS = the real memory (resident set) size of the process (in 1024 byte units)
javamem="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $6}')"

# Get Java’s PID (2nd column of ps)
javapid="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $2}')"

# Get current time
currenttime="$(date +%H%M)"

# If Java is using over 200 MB of RAM …
if [ $javamem -gt 204800 ] ; then 
  # … & if it’s between 1 & 9 a.m., then do nothing
  if [ $currenttime -gt 0100 ] && [ $currenttime -lt 0900 ] ; then
    sleep 3
  else
# … but if it’s NOT between 1 & 9 a.m., then kill the java process
kill $javapid
  fi
fi

That works, but it could be better. My good buddy Robert Citek suggested this instead:

#!/bin/bash

# Figure out how much RAM Java is using
# Uses the RSS column of ps (#6)
# RSS = the real memory (resident set) size of the process (in 1024 byte units)
javamem="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $6}')"

# Get Java’s PID (2nd column of ps)
javapid="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $2}')"

# Get current time
currenttime="$(date +%H%M)"

# If Java is using over 200 MB of RAM & it’s not between 1 & 9 a.m., kill it
if [ $javamem -gt 204800 ] && ( [ $currenttime -lt 0100 ] || [ $currenttime -gt 0900 ] ) ; then
  kill $javapid
fi

Just to review:

  • The [ ] is a bash builtin for the test command.
  • The && means that bash should execute the next command only if the previous completes successfully.
  • The ( ) groups tests.
  • The || is the OR list control operator.

That’ll work, & it’s shorter, but Robert took it even one step further:

#!/bin/bash

# Figure out how much RAM Java is using
# Uses the RSS column of ps (#6)
# RSS = the real memory (resident set) size of the process (in 1024 byte units)
javamem="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $6}')"

# Get Java’s PID (2nd column of ps)
javapid="$(ps aux | grep java | grep CrashPlan | awk -F' ' '{print $2}')"

# Get current time
currenttime="$(date +%H%M)"

# If Java is using over 200 MB of RAM & it’s not between 1 & 9 a.m., kill it
[ $javamem -gt 204800 ] && ( [ $currenttime -lt 0100 ] || [ $currenttime -gt 0900 ] ) && kill $javapid

Robert, that last one is goooood. I like it! Short & sweet & clever. That one’s the ticket!

  1. It is ridiculously stupid that launchd does not support setting a range of time.