例如,对所有含有超过100个字符的String运行OQL查询看起来如下:
select s from java.lang.String s where s.count >= 100
结果作为对象链接显示,然后展示该对象的完整内容,字段引用作为可以解除引用的其他链接的其他对象。OQL查询同样可以调用对象的方法,将正则表达式作为查询的一部分,并使用内置查询工具。一种查询工具,referrers()函数,显示了引用指定类型对象的所有引用。下面是寻找所有参考File对象的查询:
select referrers(f) from java.io.File f
您可以查找OQL的完整语法及其在jhat浏览器环境内“OQL Help”页面上的特性。将jhat与OQL相结合是对行为不当的堆进行对象调查的有效方法。
结束语
当您需要近距离观察Java进程内发生的事情时,JDK的分析扩展会非常有用。本文中介绍的所有工具都可以从命令行中由其自己使用。它们还可以与JConsole或VisualVM有力地结合使用。JConsole和VisualVM提供Java虚拟机的总体视图,jstat和jmap等有针对性的工具支持您对研究进行微调。
Java平台上更简单的脚本编写方法
现在,许多Java开发人员都喜欢在Java平台中使用脚本语言,但是使用编译到Java字节码中的动态语言有时是不可行的。在某些情况中,直接编写一个Java应用程序的脚本部分或者在一个脚本中调用特定的Java对象是更快捷、更高效的方法。
这就是javax.script产生的原因了。Java Scripting API是从Java 6开始引入的,它填补了便捷的小脚本语言和健壮的Java生态系统之间的鸿沟。通过使用Java Scripting API,您就可以在您的Java代码中快速整合几乎所有的脚本语言,这使您能够在解决一些很小的问题时有更多可选择的方法。
1.使用jrunscript执行JavaScript
每一个新的Java平台发布都会带来新的命令行工具集,它们位于JDK的bin目录。Java 6也一样,其中jrunscript便是Java平台工具集中的一个不小的补充。
设想一个编写命令行脚本进行性能监控的简单问题。这个工具将借用jmap(见本系列文章前一篇文章中的介绍),每5秒钟运行一个Java进程,从而了解进程的运行状况。一般情况下,我们会使用命令行shell脚本来完成这样的工作,但是这里的服务器应用程序部署在一些差别很大的平台上,包括Windows®和Linux®。系统管理员将会发现编写能够同时运行在两个平台的shell脚本是很痛苦的。通常的做法是编写一个Windows批处理文件和一个UNIX®shell脚本,同时保证这两个文件同步更新。
但是,任何阅读过The Pragmatic Programmer的人都知道,这严重违反了DRY (Don't Repeat Yourself)原则,而且会产生许多缺陷和问题。我们真正希望的是编写一种与操作系统无关的脚本,它能够在所有的平台上运行。
当然,Java语言是平台无关的,但是这里并不是需要使用“系统”语言的情况。我们需要的是一种脚本语言—如,JavaScript。
清单1显示的是我们所需要的简单shell脚本:
清单1. periodic.js
while (true)
{
echo("Hello, world!");
}
由于经常与Web浏览器打交道,许多Java开发人员已经知道了JavaScript(或ECMAScript;JavaScript是由Netscape开发的一种ECMAScript语言)。问题是,系统管理员要如何运行这个脚本?
当然,解决方法是JDK所带的jrunscript实用程序,如清单2所示:
清单2. jrunscript
C:\developerWorks\5things-scripting\code\jssrc>jrunscript periodic.js
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
...
注意,您也可以使用for循环按照指定的次数来循环执行这个脚本,然后才退出。基本上,jrunscript能够让您执行JavaScript的所有操作。惟一不同的是它的运行环境不是浏览器,所以运行中不会有DOM。因此,最顶层的函数和对象稍微有些不同。
因为Java 6将Rhino ECMAScript引擎作为JDK的一部分,jrunscript可以执行任何传递给它的ECMAScript代码,不管是一个文件(如此处所示)或是在更加交互式的REPL(“Read-Evaluate-Print-Loop”)shell环境。运行jrunscript就可以访问REPL shell。
2.从脚本访问Java对象
能够编写JavaScript/ECMAScript代码是非常好的,但是我们不希望被迫重新编译我们在Java语言中使用的所有代码—这是违背我们初衷的。幸好,所有使用Java Scripting API引擎的代码都完全能够访问整个Java生态系统,因为本质上一切代码都还是Java字节码。所以,回到我们之前的问题,我们可以在Java平台上使用传统的Runtime.exec()调用来启动进程,如清单3所示:
清单3. Runtime.exec()启动jmap
var p = java.lang.Runtime.getRuntime().exec("jmap", [ "-histo", arguments[0] ])
p.waitFor()
数组arguments是指向传递到这个函数参数的ECMAScript标准内置引用。在最顶层的脚本环境中,则是传递给脚本本身的的参数数组(命令行参数)。所以,在清单3中,这个脚本预期接收一个参数,该参数包含要映射的Java进程的VMID。
除此之外,我们可以利用本身为一个Java类的jmap,然后直接调用它的main()方法,如清单4所示。有了这个方法,我们不需要“传输”Process对象的in/out/err流。
清单4. JMap.main()
var args = [ "-histo", arguments[0] ]
Packages.sun.tools.jmap.JMap.main(args)
Packages语法是一个Rhino ECMAScript标识,它指向已经Rhino内创建的位于核心java.*包之外的Java包。
3.从Java代码调用脚本
从脚本调用Java对象仅仅完成了一半的工作:Java脚本环境也提供了从Java代码调用脚本的功能。这只需要实例化一个ScriptEngine对象,然后加载和评估脚本,如清单5所示:
清单5. Java平台的脚本调用
本文来源:ITeye博客 作者:佚名