02 Feb 2008
JVM Lies : The OutOfMemory Myth
It’s also been covered (with even more comments) over on TSS.
It’s certainly beneficial for a developer to have a basic understanding of Java’s approach to memory management. This post (and comments) does provide a simple overview and discusses some of the causes and gotchas for those ever annoying OutOfMemory exceptions.
If there’s one thing to take note of, it’s that not all memory problems can be solved by an increase in the heap allocation. So please don’t do that and just walk away, it’s never a one size fits all problem and will almost certainly come back to bite you.
(If you’re like me, I’m sure you all love a good PermGen-related OOM… uhh… maybe not.)
24 Jan 2008
In Leopard, Apple has introduced a new mechanism for managing and maintaining your system path ($PATH).
Previously (and in most current Linux environments) paths were managed by updating the PATH environment variable directly in either the system profile (/etc/profile) or your local profile (~/.bash_profile).
Commonly you had entries like:
export JAVAHOME = /usr/lib/j2se/jdk1.5.013/
export PATH=$PATH:$JAVA_HOME/bin
...
In Leopard, you no longer have to modify the profile to make adjustments to system paths. Instead, you can put a simple text file containing a path entry (or entries) into /etc/paths.d/.
Each line in this file will be interpreted as a path and added automatically to the system path.
macos-gls000120:~ ajordens$ ls -al /etc/paths.d/
total 40
drwxr-xr-x 7 root wheel 238 Jan 22 18:51 .
drwxr-xr-x 105 root wheel 3570 Jan 23 11:59 ..
-rw-r--r-- 1 root wheel 13 Sep 23 20:53 X11
-rw-r--r-- 1 root wheel 16 Jan 22 18:58 groovy
-rw-r--r-- 1 root wheel 15 Jan 22 18:58 maven
-rw-r--r-- 1 root wheel 20 Jan 22 18:58 postgresql
-rw-r--r-- 1 root wheel 15 Jan 22 18:58 scala
macos-gls000120:~ ajordens$ cat /etc/paths.d/scala
$SCALA_HOME/bin
It’s up to you whether you include an environment variable or specify the full path.
Enter Leopard
In order to take advantage of this new feature, your /etc/profile needs to invoke /usr/libexec/path_helper. Depending on your upgrade path to Leopard, your profile may or may not have been automatically updated.
Unfortunately mine wasn’t and I couldn’t find a definitive post telling what changes I had to make to the profile. I decided to write the procedure up so that anyone else wanting to take advantage of paths.d and path_helper could do so.
macos-gls000120:~ ajordens$ cat /etc/profile
System-wide .profile for sh(1)
if [ "${BASH-no}" != "no" ]; then
[ -r /etc/bashrc ] && . /etc/bashrc
fi
export ANT_OPTS="-Xmx256M"
export POSTGRESQLHOME="/opt/local/lib/postgresql82"
export CONFBASE=/usr/local/src/confluence-2.6.2
export HADOOPHOME=/usr/local/src/hadoop-0.15.2
export GROOVYHOME=/usr/local/src/groovy-1.5.1
export SCALAHOME=/usr/local/src/scala-2.6.0-final
export MAVEN_HOME=/usr/local/src/maven-2.0.7
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/"
if [ -x /usr/libexec/pathhelper ]; then
eval `/usr/libexec/pathhelper -s`
fi
That’s basically it. I put the path_helper invocation at the end of the profile because I wanted to be able to substitute environment variables.
21 Jan 2008
I’ve been working my way through Working Effectively with Legacy Code and one of their strategies for testing classes has particularly hit home.
Situation
You’ve fixed a bug in a method that lacks any test cases and is not easily incorporated into a test harness.
More often than not this method is private (because we all test our public methods right?)
The obvious and straight-forward approach to testing (in this situation) would be to write a test case against whatever public method ultimately results in the problem code being executed.
Unfortunately, if the public method was easily tested we hopefully wouldn’t be finding ourselves in this situation (because we’d either have had test cases that already executed the problem code – or they’d be easily added).
What you’re often faced with is a combination of two things, local state maintained by the class (pesky instance variables) and state passed in to the method as parameters. With appropriate use of stubs and mock objects, the latter should be workable. However, unless you’re dealing with a stateless class the matter of local state is an annoying yet unavoidable complication.
What follows is one idea that I’ve seen work quite successfully in these types of situations.
Step 1: The Minor Refactor
This initial refactor involves extracting the pertinent portion of business logic (that you want to put under test) and any instance-level state that it requires. Often this is as simple as extracting a private method and passing any required class variables/state in as parameters. What you should be left with is a method that has all of it’s dependencies provided to it, this is important.
private void updateSpinner(List xyzs)
{
// this map should contain 1 and only 1 spinner
JSpinner spinner = instanceSpinnerMap.values().iterator().next();
spinner.setValue(xyzs.size());
spinner.setEnabled(false);
}
becomes
private void updateSpinner(List xyzs, JSpinner spinner)
{
spinner.setValue(xyzs.size());
spinner.setEnabled(false);
}
Fairly trivial change and significantly more testable (ignoring the fact that it’s still a private method).
Step 2: A little less private
Unfortunately, private methods aren’t the easiest things in the world to test. You may not like what I’m going to suggest next… but depending on the situation, you may get a lot of mileage out of making that private method package-private instead. If you’re already programming against interfaces and have a decent package layout, you likely won’t run into too many problems.
Step 3: Write test cases
Now that you’re able to instantiate the class and invoke it’s new package-private utility method, there’s no excuse not to write a couple of unit tests against it.
Step 4: Updated Functionality
Obviously there was a method to our madness and the whole reason we under took this exercise was the change the functionality of this class/method.
Depending on your approach to testing (TDD or TSA – test soon after) you should now be able to update the functionality of the method and write new test cases.
Step 5: Run Tests
Run both the previous and new test cases to verify that you haven’t broken previous functionality and that it also supports the new capabilities you just finished adding.
All in all, I’ve seen this work quite successfully. With a little discipline, there’s no reason why we can’t get more of the code we’re writing under test. Of course this isn’t a replacement for interface-level testing nor a reason to throw the private modifier out the window, but rather another idea to keep in the back of your mind as your bug fixing or making minor feature changes.