Thursday, August 21, 2008

Fixing Java Memory Tools on Mac OS X

A number of the Java memory management tools with the default distribution on Mac OS X Leopard are broken. The information to repair the situation seems to be hard to come by. This post will detail the steps necessary to get jmap and jhat to work on a Mac for Java 6. I assume that Java 5 has the same issue but haven't checked.


Setting up a test Java process to profile.
1. From the terminal: cd /Developer/Examples/Java/JFC/Java2D/
2. From the terminal: java -jar Java2D.jar &
3. After the process starts it should indicate the pid (in this example it is 5510)
4. You can also use jps to discover the pid: jps -l (then look for the Java2D.jar)

Fixing jmap
If you type jmap , you would expect to get a high-level memory dump... instead you get a message like:
attach: task_for_pid(5510) failed (5)
Error attaching to process: Error attaching to process, or no such process

To fix this, do a sudo, such as sudo jmap .
Debugger attached successfully.
Server compiler detected.
JVM version is 1.6.0_05-b13-52

using thread-local object allocation.
Mark Sweep Compact GC

Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 88080384 (84.0MB)
NewSize = 2686976 (2.5625MB)
MaxNewSize = -65536 (-0.0625MB)
OldSize = 5439488 (5.1875MB)
NewRatio = 2
SurvivorRatio = 6
PermSize = 21757952 (20.75MB)
MaxPermSize = 88080384 (84.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 10551296 (10.0625MB)
used = 4592152 (4.379417419433594MB)
free = 5959144 (5.683082580566406MB)
43.52216069002329% used

At this point it looks like it is working, however the file dump doesn't work.

Fixing jhat
When running jhat on the hump dump, you get the following error:
Eliminating duplicate references.....................
Snapshot resolved.
Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException
at com.sun.tools.hat.internal.oql.OQLEngine.init(OQLEngine.java:277)
at com.sun.tools.hat.internal.oql.OQLEngine.(OQLEngine.java:51)
at com.sun.tools.hat.internal.server.QueryListener.setModel(QueryListener.java:59)
at com.sun.tools.hat.Main.main(Main.java:189)
Caused by: java.lang.NullPointerException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.tools.hat.internal.oql.OQLEngine.init(OQLEngine.java:256)
... 3 more

It took me a little hunting to uncover the issue. It turns out that jhat uses the Java 6 js engine. If you go to the terminal again and run: jrunscript , it indicates that the script engine for language js can not be found. On the mac, if you execute: jrunscript -q, which provides a listing of script engines you'll see:
Language AppleScript 2.0.1 implemention "AppleScriptEngine" 1.0
Thank you apple!

As a side note if you actually wanted to run AppleScript as a shell, type jrunscript -l AppleScript

Let's get to fixing the issue
  1. Download JSR-223's engines https://scripting.dev.java.net/files/documents/4957/37593/jsr223-engines.zip
  2. Download Rhino http://www.mozilla.org/rhino/download.html
  3. Copy jsr223-engines/javascript/build/js-engine.jar to /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/
  4. Copy rhino1_7R1/js.jar to /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/lib/ext/
** you have to be sudo to do steps 3 and 4.

now jrunscript -q should look like this:
Language EmbeddedECMAScript 1.6 implemention "Mozilla Rhino" 1.6 release 2
Language AppleScript 2.0.1 implemention "AppleScriptEngine" 1.0
Language ECMAScript 1.6 implemention "Mozilla Rhino" 1.6R7

... and jrunscript will put you in a js shell.
... and jhat heap.out now works! Point your browser at http://localhost:7000 and object browse!

Also something I didn't realize until I was preparing for this post... jmap worked for the console dump if I sudo'd the command. However it would not do a file dump. I did the file dump using jconsole and jmx. Now that the scripting issue is fixed, jmap is improved in the following manner:
  1. if you type jmap , it now prompts you for your admin password (instead of just failing)
  2. jmap -dump:live,format=b.file=heap.out 5510 now works

It makes me wonder what else wasn't working! Happy Coding!

10 comments:

Stu Thompson said...

You are officially "Internet Hero of the Week". This post saved me time mucking about, and kept on tract to debug own application. Thanks a zillion. If you ever find yourself in Zürich, let me know...I owe you a beer!

Tobias Zeck said...

Thank you very much for this blog. You saved me so much time to get jhat running on Leopard. Now i am able to debug my issue about PermGen and Classloader leaks.

If you are visiting germany I owe you a beer too.

Markus Kohler said...

Hi all,
Instead of using jhat I would suggest you to check the Eclipse Memory Analyzer.

It's really (attention, I'm biased ;) ) much better than JHAT.

interbine said...

Definitely the "Hero of the Week".

You have a beer waiting for you in Tel Aviv too...

Ori

Charles Oliver Nutter said...

Another kudos from me...I never would have figured this out. Now we just need to get Apple to ship a non-broken Java 6.

Jonathan Fuerth said...

Wow, thanks for taking the time to explain how to fix this.

To add to your "free beer tour of the world," we have a beer waiting for you in Toronto.

Bruno Duarte said...

Thank you for the fix.

You have a grapes juice waiting for you in Cambridge.

aaronyo said...

And there's now a micro brew waiting for you in Bend, OR, USA. Thanks for the time saver.

Kedar Mhaswade said...

Kudos!

You are an Internet Hero of another week. This is April 2009. Thank you!

-Kedar

Nate said...

Awesome, thanks! I doubt I would have been able to figure this out on my own.