I've been battling getting jmap working properly on a new Mac. I followed the invaluable instructions my NFJS colleague, Ken Sipe, provided for the Rhino engine inclusion. However, I was still encountering an odd issue where I couldn't get jmap to attach to a Java process.
First, I would start a simple helloworld Java application.
[bash] java com.ambientideas.HelloWorldJava Hello Java World! [/bash]
Then I would get the PID:
[bash] jps 11554 HelloWorldJava 11573 Jps [/bash]
Then I tried to jmap it, but got the following error
[bash] jmap -heap 11554 Attaching to process ID 11791, please wait... attach: task_for_pid(11791) failed (5) Error attaching to process: Error attaching to process, or no such process [/bash]
So I thought, "These memory tools often require root access, so I'll sudo it." When I did, I got the following equally cryptic error:
[bash] sudo jmap -heap 11791 Attaching to process ID 11791, please wait... Exception in thread main java.lang.RuntimeException: gHotSpotVMTypes was not initialized properly in the remote process; can not continue at sun.jvm.hotspot.HotSpotTypeDataBase.readVMTypes(HotSpotTypeDataBase.java:111) at sun.jvm.hotspot.HotSpotTypeDataBase.<init>(HotSpotTypeDataBase.java:68) at sun.jvm.hotspot.MacOSXTypeDataBase.<init>(MacOSXTypeDataBase.java:35) at sun.jvm.hotspot.bugspot.BugSpotAgent.setupVM(BugSpotAgent.java:560) at sun.jvm.hotspot.bugspot.BugSpotAgent.go(BugSpotAgent.java:481) at sun.jvm.hotspot.bugspot.BugSpotAgent.attach(BugSpotAgent.java:319) at sun.jvm.hotspot.tools.Tool.start(Tool.java:146) at sun.jvm.hotspot.tools.JMap.main(JMap.java:128) [/bash]
I though, "Hmmmm, this looks a lot like a java version mismatch. Let's check which java version I'm running." I had javac compiled the HelloWorld against Java 6, so was expecting a matching java. I made a quick stop into the Java Preferences pane. Yup. Java 6 set as the first-priority choice.
Let's check the command line too...
[bash] java -version java version 1.6.0_13 Java(TM) SE Runtime Environment (build 1.6.0_13-b03-211) Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02-83, mixed mode) [/bash]
Excellent. Just what I expected. Java 6. But a little doubt crept into my mind. What if the sudo'ed invocation were finding a different Java. That didn't sound plausible, but yet was still worth checking out.
[bash] sudo java -version java version 1.5.0_19 Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_19-b02-304) Java HotSpot(TM) Client VM (build 1.5.0_19-137, mixed mode, sharing) [/bash]
Aha! There's the culprit. So somehow, sudo'ed commands are running Java 5, even though I've set a preference for 6 in my profile. I searched the web high and low for a way to specify the Java version for "sudo" specifically, but could find no trace. I even tried the -E flag to maintain the caller's environment variables, as is possible on Linux distributions. Not implemented on Mac.
I did a quick check of my path and a "which java". Both ultimately routed to Java 6. I also had JAVA_HOME and JAVA_VERSION set just fine.
[bash] set | grep JAVA JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home JAVA_VERSION=1.6 echo $PATH /Applications/Dev/btrace/bin:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin: /usr/X11/bin:./: /Users/mccm06/scripts:/Applications/Dev/apache-ant/bin: /Applications/Dev/apache-maven/bin [/bash]
So I turned to harness the wisdom of one of the really smart guys (that uses a Mac) I'm privileged to hang out with in Denver, Fred Jean. Fred said, "Try adding the 1.6 java explicitly to the front of your path rather than letting Java Preferences pane be the sole controller." I opened up ~/.bash_profile (alternatively you could edit ~/.bash_rc)and slammed in the following:
[bash] export PATH=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Commands:$PATH [/bash]
I opened a new terminal to pick up the shell changes. Then I started checking if this fixed the issue in reverse order, starting with the sudo:
[bash] sudo java -version java version 1.6.0_13 Java(TM) SE Runtime Environment (build 1.6.0_13-b03-211) Java HotSpot(TM) 64-Bit Server VM (build 11.3-b02-83, mixed mode) [/bash]
Looking good! Now let's get back to our core goal, jmap.
[bash] sudo jmap -heap 5951 Attaching to process ID 5951, please wait... Debugger attached successfully. Server compiler detected. JVM version is 11.3-b02-83 using parallel threads in the new generation. using thread-local object allocation. Concurrent Mark-Sweep GC Heap Configuration: MinHeapFreeRatio = 40 MaxHeapFreeRatio = 70 MaxHeapSize = 88080384 (84.0MB) NewSize = 21757952 (20.75MB) MaxNewSize = 43581440 (41.5625MB) OldSize = 64159744 (61.1875MB) NewRatio = 7 SurvivorRatio = 6 PermSize = 21757952 (20.75MB) MaxPermSize = 88080384 (84.0MB) Heap Usage: New Generation (Eden + 1 Survivor Space): capacity = 19070976 (18.1875MB) used = 2351616 (2.24267578125MB) free = 16719360 (15.94482421875MB) 12.330863402061855% used Eden Space: capacity = 16384000 (15.625MB) used = 2351616 (2.24267578125MB) free = 14032384 (13.38232421875MB) 14.353125% used From Space: capacity = 2686976 (2.5625MB) used = 0 (0.0MB) free = 2686976 (2.5625MB) 0.0% used To Space: capacity = 2686976 (2.5625MB) used = 0 (0.0MB) free = 2686976 (2.5625MB) 0.0% used concurrent mark-sweep generation: capacity = 64159744 (61.1875MB) used = 0 (0.0MB) free = 64159744 (61.1875MB) 0.0% used Perm Generation: capacity = 21757952 (20.75MB) used = 4716880 (4.4983673095703125MB) free = 17041072 (16.251632690429688MB) 21.678878600338855% used [/bash]
Thanks to a combination of Ken Sipe, Fred Jean, a few good guesses and some investigative work, the problem is solved! I'm longwindedly calling this the "java version is different for sudo-initiated processes on Mac" so that the next person Googling for this using similar keywords will hopefully find this tutorial.
As a closing note, I often get asked how I set my environment variables for Java so that Spotlight-launched, Quicksilver-launched, Dock-launched, and Terminal-launched processes can all see the variables (yes, there's some odd pitfalls about that on a Mac due to the process parents being different). The short answer is to create a text file called /private/etc/launchd.conf and put the following (applicable portions, based on your tool usage) contents in it:
[bash] setenv JAVA_VERSION 1.6 # A) Some process-launchers will cause their child to default to 1.5 if using /Library/Java/Home # even if Java Preferences pane says 1.6 at the top # setenv JAVA_HOME /Library/Java/Home # B) Alternate: Force Java 1.6 as Java Home setenv JAVA_HOME /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home setenv GROOVY_HOME /Applications/Dev/groovy setenv GRAILS_HOME /Applications/Dev/grails setenv NEXUS_HOME /Applications/Dev/nexus/nexus-webapp setenv JRUBY_HOME /Applications/Dev/jruby # Set up Ant setenv ANT_HOME /Applications/Dev/apache-ant setenv ANT_OPTS -Xmx512M # Set up Maven setenv MAVEN_OPTS -Xmx1024M setenv M2_HOME /Applications/Dev/apache-maven [/bash]
As we have learned the hard way, the only pitfall is that the java version specified in launchd.conf is not honored by sudo'ed java invocations.
Drop me an email if you've seen this issue and this tutorial helps you solve it.