In one of the recent e-mails, Frank Kieviet suggested an improvement for jhat tool in JDK 6. He said something like: Sundar: another idea to extend jhat: when looking at a memory dump and looking at the routes to a particular classloader, there are sometimes hundreds of references. [...] jhat could print some histogram data so that it’s immediately apparent which object reference is suspect.
The above could be generalized to any Java object (not just a classloader object). "routes to a particular classloader" probably requires explanation. Every Java object is live because there are one or more paths or routes (or chain of references) from the GC root set [recall that the root set is the initial object references from which garbage collector starts and constructs transitive closure of all objects reachable from there - such transitively reachable objects are alive and hence not eligible for collection]. Frank wants a facility in jhat to view histogram of objects across the liveness paths of a given object.
Being lazy, I don't want to change jhat for this
[also users may not be willing to move to a different release of JDK just for this jhat change!] Fortunately, jhat has a query interface called OQL.
OQL is built on top of the JavaScript engine bundled in JDK 6. Actually, you can even run any JavaScript code (and not just select...from..where style OQL queries) in jhat's OQL window! If the string entered in OQL window does not start with the "select" keyword, it is treated as JavaScript code and eval'ed as is using the JavaScript script engine bundled in JDK 6.
OQL has number of built-in JavaScript functions and objects. There is one object called "heap" which has useful methods such as "livepaths", "findObject". "livepaths" methods returns an array of paths by which given Java object is alive. Each path is another array of objects [objects in that liveness path chain]. There is another method called "findObject" which returns a specific object given it's object identifier [note that jhat shows object ids in hex format - so the hex format is used by "findObject" as well]. Using these we can write something like this:
(function() {
var histo = new Object();
map(heap.livepaths(heap.findObject('0x0f054b90')),
function(path) {
for (var i = 0; i < path.length; i++) {
var desc = toHtml(path[i]);
if (desc in histo) {
histo[desc]++;
} else {
histo[desc] = 1;
}
}
});
var str = "<table>";
for (i in histo) {
str += "<tr>";
str += "<td>" + i + "</td><td>" + histo[i] + "</td></tr>";
}
str += "</table>";
return str;
})();
In the above code, the object id '0x0f054b90' used as argument to heap.findObject method is an object of interest in a specific heap dump. This is an object whose liveness paths we are interested on [turns out to be a classloader object]. When I entered the above script in jhat's OQL window, I got a nice histogram in HTML table form!
Continuation is an object that represents the execution state of a program at a certain point. We can use continuation to restart the execution from the point stored in it.
How about continuations for the Java platform? There are atleast two different implementations of continuations:
I've experimented with javaflow. I checked out javaflow sources under, say %JAVAFLOW_HOME% directory (no pre-built binaries available in the site). I tried building it by maven. But failed ... because I need parent pom file! (never mind if you don't understand that -- that is just a build step). Because I am only interested in playing with continuation, I ignored maven build. I just created a NetBeans project and added all source directories of javaflow. I copied dependent libraries (ant.jar, commons-logging-1.0.4.jar, junit-3.8.2.bar, junit-addons-1.4.jar -- I just copied the versions that I had -- check for proper dependency or use maven to build!) under %JAVAFLOW_HOME%\lib directory and added all jars under this directory to netbeans project. I managed to build and produce javaflow.jar under %JAVAFLOW_HOME%\dist. The following is a simple program that uses continuations [this is just slightly modified version of the one in the javaflow tutorial]
import org.apache.commons.javaflow.*;
class Test {
static class MyRunnable implements Runnable {
public void run() {
System.out.println("run started!");
for( int i=0; i < 10; i++ ) {
echo(i);
}
}
private void echo(int x) {
System.out.println("echo " + x);
Continuation.suspend();
}
}
public static void main(String[] args) {
System.out.println("main started");
Continuation c = Continuation.startWith(new MyRunnable());
System.out.println("in main after continuation return");
while (c != null) {
c = Continuation.continueWith(c);
System.out.println("in main");
}
}
}
The output of the above program is shown below:
main started run started! echo 0 in main after continuation return echo 1 in main echo 2 in main echo 3 in main echo 4 in main echo 5 in main echo 6 in main echo 7 in main echo 8 in main echo 9 in main in main
The execution seems to "flip-flop" between Test.main and
Test.MyRunnable.echo methods!. No, there are no
multiple threads here. Single thread of execution produces
the output shown above. That is because of continuation. If you don't get that,
you may want to read this.
The steps used in build, run the above program:
javac -cp %JAVAFLOW_HOME%\dist\javaflow.jar Test.javaThe above step created two .class files - Test.class, Test$MyRunnable.class.
Continuation implementation works by bytecode instrumenting of the .class files of your application. Either you can instrument the program ahead of execution (during build) or do it at runtime using a special class loader. This being a simple hack exercise for learning, I instrumented these .class files using the following commands:
java -cp .;%JAVAFLOW_HOME%\dist\javaflow.jar;%JAVAFLOW_HOME%\lib\* RewriteTool <Test.class >mTest.class java -cp .;%JAVAFLOW_HOME%\dist\javaflow.jar;%JAVAFLOW_HOME%\lib\* RewriteTool <Test$MyRunnable.class >mTest$MyRunnable.class move mTest.class Test.class move mTest$MyRunnable.class Test$MyRunnable.class
Actually, there is a better way to pre-instrument .class files for continuation support: there is a javaflow ant task. (the ones that use continuations need to be instrumented. You can leave the other jar files)
java -cp .;%JAVAFLOW_HOME%\dist\javaflow.jar;%JAVAFLOW_HOME%\lib\* Test
As you may know already, JDK 6 includes javax.script API
and Mozilla Rhino based
JavaScript engine. There is continuation support in Rhino. But then, that is
a topic for another blog entry
SVG - Scalable Vector Graphics is a modular language for describing two-dimensional vector and mixed vector/raster graphics in XML. Batik is a Java-based toolkit for applications that want to use images in the SVG format for display, generation or manipulation. Batik supports script tag of SVG. The script tag can be used to implement interactivity, animation and so on. Batik uses Mozilla Rhino based JavaScript implementation with script tag implementation.
I've just added support for using any jsr-223 compatible scripting languages (like the ones at scripting.dev.java.net) with SVG. The source for SVG jsr-223 script extension is available at scripting.dev.java.net project. You may want to refer to README.TXT of this project.
JDK 6 includes Mozilla Rhino based implementation of javax.script API for JavaScript. There are javax.script engines for other scripting languages are available at scripting.dev.java.net. The scripting languages for the Java platform offer someway of accessing Java classes from scripts. Most of these scripting languages being dynamically typed, user does not supply type information -- which makes it difficult to interact with Java classes. For example, method overload resolution - which is done by javac - has to be done using dynamic type of the arguments in scripting languages. Scripting languages choose their own policy to pick the "correct" overload variant when a script calls a (overloaded) Java method. Yesterday, Michael Nascimento Santo reported a bug against JavaScript engine in JDK 6. I believe this is not a bug.
First, this is not an issue with javax.script implementation on top of Rhino. This can be seen by running Rhino's shell [which comes with Mozilla Rhino download] - avoiding the jsr-223 API cover. Using Rhino 1.6R2 [which is the version bundled with JDK 6], I got the following result:
D:\rhino1_6R2>java -cp js.jar;. org.mozilla.javascript.tools.shell.Main
Rhino 1.6 release 2 2005 09 19
js> p = new Packages.FunctionsClass()
FunctionsClass@f81843
js> p.equals("x", "x")
js: "<stdin>", line 3: Can't find method java.lang.Object.equals(string,string).
js: "<stdin>", line 3: org.mozilla.javascript.EvaluatorException: Can't find met
hod java.lang.Object.equals(string,string). (<stdin>#3)
js> Packages.FunctionsClass.equals('x', 'x')
true
Clearly, Rhino itself does not find that particular static method when calling through an instance. I believe this is a design choice by Rhino. Please refer to bullet 2 of section 3.1 of http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html.
All scripting languages for the Java platform have to choose some policy for method overload resolution. If the invokedynamic bytecode has to handle overloading at runtime, you can imagine how problematic that would be! JVM has to make the overload resolution at runtime using the dynamic types of arguments (consider boxing/unboxing too)! Apart from possible performance issues, the policy chosen for the overload resolution may "hurt" some scripting language. I think most likely the invokedynamic bytecode would require method signature -- thereby leaving the overload resolution to the scripting language runtime.
These days I learn Java APIs by exploratory programming using the new scripting feature in JDK 6. I try interactively out using jrunscript . I wanted to try out the JSR-231 (Java Binding for the OpenGL) API.
jrunscript -J-Djava.library.path=%JOGL_HOME%/lib -cp %JOGL_HOME%/lib/jogl.jar
-J-Djava.library.path option is used so that JVM can load JOGL native DLLs and
-cp jogl.jar is used to put jogl classes in the CLASSPATH. With this command line, jrunscript waits for interective user input with "js>" prompt.
js> importPackage(javax.media.opengl)
js> println(new GLCapabilities())
GLCapabilities [DoubleBuffered: true, Stereo: false, HardwareAccelerated: true,
DepthBits: 24, StencilBits: 0, Red: 8, Green: 8, Blue: 8, Alpha: 0, Red Accum: 0
, Green Accum: 0, Blue Accum: 0, Alpha Accum: 0, Multisample: false ]
js>
So, looks like we can access JSR-231 classes successfully. Now, to some "hello worlds" with OpenGL binding...
// import JSR-231 stuff
importPackage(javax.media.opengl);
importClass(javax.swing.JFrame);
var frame = new JFrame("hello");
frame.setSize(400, 400);
//create GLCanvas and add that to frame
var glcanvas = new GLCanvas();
frame.add(glcanvas);
glcanvas.addGLEventListener(new GLEventListener() {
init: function(drawable) {
},
display: function(drawable) {
var gl = drawable.getGL();
// with this "with" statement, we don't need to prefix
// "gl." before GL method calls -- so that it feels like
// writing in C
with(gl) {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_POLYGON);
glVertex3f(0.25, 0.25, 0.0);
glVertex3f(0.75, 0.25, 0.0);
glVertex3f(0.75, 0.75, 0.0);
glVertex3f(0.25, 0.75, 0.0);
glEnd();
glFlush();
}
},
reshape: function() {},
displayChanged: function() {}
});
frame.setVisible(true);
File: circle.js
importPackage(javax.media.opengl);
importClass(javax.swing.JFrame);
var frame = new JFrame("hello");
frame.setSize(400, 400);
var glcanvas = new GLCanvas();
frame.add(glcanvas);
glcanvas.addGLEventListener(new GLEventListener() {
init: function(drawable) {
},
display: function(drawable) {
var gl = drawable.getGL();
with(gl) {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for (var i = 0; i < 100; i++) {
var angle = 2*Math.PI*i / 100;
glVertex2f(Math.cos(angle), Math.sin(angle));
}
glEnd();
glFlush();
}
},
reshape: function() {},
displayChanged: function() {}
});
frame.setVisible(true);
load function.
js>load("rect.js");
js>load("circle.js");
What happened to JVMPI in JDK 6? Kelly O'Hair explains here. If you have code that uses JVMPI, you may want to refer to Kelly's article: The JVMPI Transition to JVMTI
Updated JSR-223 script engines @ http://scripting.dev.java.net for Groovy and JRuby to use the latest versions [Groovy 1.0 and JRuby 0.9.2 respectively]. Thanks to Danny Lagrouw and Mario Aquino for (independently) submitting changes to JRuby script engine due to interface changes in JRuby 0.9.2.
And belated Happy new year!. Best wishes for succeeding with your new year resolution(s), if any. As usual, I'm sticking to my (meta) new year resolution - which is "never take new year resolutions"