[转载]进入 Harmony 世界,第 3 部分: Harmony VMI

进入 Harmony 世界,第 3 部分: Harmony VMI


Apache Harmony 是 2005 年 5 月宣布的开放源码 Java SE 实现,本文是由 5 部分组成的 进入 Harmony 世界 系列文章的第三篇,这个系列主要介绍 Apache Harmony 项目的内部实现,最新发展现状和开源 Java 开发的模式,并鼓励和欢迎大家参与到 Harmony 的社区中来。

作为由 5 个部分组成的系列文章的第三部分,这篇文章较详细地介绍了 Harmony 项目中的虚拟机接口(VMI)。

为什么需要 Harmony 虚拟机接口(VMI)

Java 规范已经定义了一些虚拟机的 API,比如 Java 本地接口(JNI), Java 虚拟机工具接口(JVMTI)等,但是 Java 规范并没有定义类库和虚拟机之间的接口。Harmony 为了模块化和可移植性的要求,定义了虚拟机接口(VMI)。VMI 的存在,使 Harmony 的 Class Lib 和虚拟机的分离成为了可能。只要实现了该接口的虚拟机,就可以和 Harmony 的类库兼容,可以与 Harmony 的类库实现互操作,并且可以与 Harmony 提供的 Java 启动程序(launcher)协作。基于这种设计,Harmony 除了实现大量类库代码之外,已经有了 J9VM,DRLVM,StableVM 等等相兼容的虚拟机,并有了很好的运作。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


什么是 VMI

本系列的上一篇“研究 Port Layer” 中提到过,开发 Java 类库必须编写本地代码以使用操作系统调用,并且介绍了如何利用 Harmony Port Library 开发平台无关的本地代码。那么如何从 Java 类库中调用这些本地代码呢?虚拟机接口(Virtual Machine Interface)就是这样一道窗户,沟通了 Java 世界和本地机器。

Apache Harmony 虚拟机接口分为三个部分,一部分是 Java 语言接口,由 23 个内核类(Kernel Class)组成,另一部分是 C 语言接口,由 10 个函数组成,最后一部分就是标准 JNI。

最 重要的,也是最早提出的 VMI 就是 Java 1.0 时代就存在的 Java 本地接口(Java Native Interface, JNI)。JNI 是这样一种的编程标准:它不仅能够将本地函数内嵌入 Java 过程之中;还能从本地程序,例如由 C 语言编写的标准 win32 程序创建 Java 虚拟机并使用 Java 功能。JNI 是强大的,它使 Java 具有了同本地代码一样全面的操纵系统的能力;它提高了 Java 系统的运行效率:如果目前的 Java,在一些特定场合,执行速度还不能像本机代码那样令人满意的话;它使 Java 系统能够继续利用业已存在的本地代码库,使得遗留系统向 Java 系统的迁移不那么痛苦。但是 JNI 也有其显著的缺点:程序不再是“100% 的纯 Java”了,牺牲了代码的可移植性——开发人员必须为这些程序在不同平台上的部署进行再测试和再调试。

此后,又提出了 JVMPI(Java Virtual Machine Profiler Interface)和 JVMDI(Java Virtual Machine Debug Interface)。JVMPI 是居于 Java 虚拟机和记录代理之间的一个函数调用接口:Java 虚拟机利用 JVMPI 通知记录代理虚拟机内部的事件:如启动线程,堆分配等等;而记录代理又可以在回调函数中控制 Java 虚拟机的行为。JVMDI 同样是一个函数接口,与 JVMPI 不同的是,JVMDI 主要是提供调试器以及其他工具一个监测以及控制 Java 虚拟机内部状态的编程接口。这是由于 JVMPI 和 JVMDI 之间的相似性,随着 Java 5 的推出,出现了一个用来取代上述两者的,更为统一的虚拟机接口:JVMTM Tool Interface (JVMTI)。JVMTI 功能更为强大,不仅局限于 JVMPI 和 JVMDI 的所有功能。事实上,Harmony 项目的 instrument 模块就是利用 JVMTI 实现的。

Harmony 的 VMI 还包括内核类。内核类的提出是因为少数核心的公共类是与虚拟机密切相关(VM-specific)的,它们都是属于 java.lang、java.lang.ref、java.lang.reflect 和 java.security 等几个核心的包,比如说 java.lang.ClassLoader、 java.lang.ref.WeakReference 等。随着 Java 版本的升级,核心类的数量也可能会增加。Harmony 的类库实现为大多数核心类定义了实现模板。VM 的开发人员可以从零开始实现这些核心类,也可以在 Harmony 提供的模板基础上开始开发。

Harmony 项目定义了自己的虚拟机接口 VMI,它与传统虚拟机结构兼容,实现了 JNI 和 JVMTI;事实上,Harmony VMI 更为强大:VMI 能够提供 Port Layer 支持,从而使 JNI 具有跨平台能力;还有另一些虚拟机提供者可选的函数接口,具有更强的扩展性。不仅如此,Harmony VMI 还是 Harmony 项目的核心,它联系了项目的最主要的三大模块:类库、虚拟机和 Port Layer。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


Harmony VMI 的特点

可移植性

与 以往的本地代码会引入平台相关性不同,Harmony VMI 提供了 Port Library,只要程序人员在本地代码中使用 Port 函数而不是直接调用操作系统 API,就能保持 Java 项目的可移植性。这一点不仅对于 Harmony 类库的开发者是重要的,而且对于 Java 应用程序员来说,Harmony VMI 的这个特点使他们能够充分利用 JNI/JNTI 的强大力量而不必担心要在不同平台上调试同一程序所带来的麻烦。至于 Harmony 项目利用 Port Library 屏蔽平台差异性的过程,在本系列的上一篇中我们已经详细描述了,这里不再赘述。

模块化

Harmony 项目从一开始,就把模块化作为设计的主要目标之一。组件之间的依赖存在于接口之间,而不是模块的实现之间。只要是一个符合 Java Class File Format 的类文件,就能运行在各个 Java 虚拟机上;所有的本地代码,只要其调用符合 VMI 规范,就能获得各个 Java 虚拟机的支持;而不同的 Port Layer 实现,只要实现 struct HyPortLibrary 定义的各个虚函数,就能满足所有本地代码的系统调用。希望能够将 A 组织提供的类库,运行在 B 厂商的 Java 虚拟机上,而本地代码调用又是通过 C 个人实现的 Port Library 进行的。事实上,Harmony 的类库已经能够运行在 IBM J9 虚拟机或者 DRLVM 虚拟机上,Harmony Port Library 也有潜在的候选者。


Apache Harmony – Architecture

Harmony 各部分的关系

Class Library、Java 虚拟机和 Port Layer 是 Harmony 项目最主要的三个组成部分。它们之间的依赖关系如下图所示:


Figure 2. VMI Architecture
Apache Harmony – Architecture

Java 类库要通过 Port Layer 屏蔽其 native code 中对系统调用的依赖。Java 虚拟机也必须执行一些操作系统相关的任务:如启动 OS 线程、分配系统堆,这些工作也要依靠 Port Layer 以保证 Java 虚拟机自身的跨平台能力。对于 Java 类库对虚拟机的依赖。一方面,所有的本地函数的声明,诸如:


本地函数声明
				
JNIEXPORT jbyteArray JNICALL Java_org_apache_harmony_luni_platform_Environment_getEnvByName
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
(JNIEnv *env, jclass obj, jbyteArray name)

其中变量 JNIEnv *env 就是 Java 虚拟机提供的 JNI 环境变量。事实上,JNI 就是一个双向接口:当某个 Java 类使用到一个本地方法的时候,Java 虚拟机就从预先已经装入的本地代码库中链接上该本地方法的实现,并将 JNI 环境变量提供给这个本地方法。而该本地方法能够通过 JNI 环境变量作为 Java 虚拟机的接口使用虚拟机的功能,乃至控制虚拟机的控制:


				
(*env)->FatalError(env,”message”)

甚至能够关闭虚拟机。

另一方面,有 这样一类 Java class,几乎整个类库都依赖于它们,而这些类又与虚拟机联系如此紧密,Harmony 项目决定由 Java 虚拟机提供它们。这些类包括 Class、Object、String 以及原始数据类型等。它们被称为 Kernel Class,因为是由虚拟机提供的,也被认为是虚拟机接口的一部分。除此以外,所有类库本地代码中使用的 Port Layer 也是通过虚拟机接口获得的。

事实上,VMI 是 Harmony 项目的核心,是联系 Class Library、Java 虚拟机和 Port Layer的桥梁。

Harmony 的 VMI 被分为 VM Interface 和 Kernel Class 两部分,接下来分别介绍它们。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


VM Interface

VM Interface 是用来沟通 Java 虚拟机和本地代码的 c 结构。在目前的 Harmony 项目的 vmi.h 中是这样定义的:


VM Interface 定义
				
struct VMInterfaceFunctions_
{
vmiError (JNICALL * CheckVersion) (VMInterface * vmi,
vmiVersion * version);
JavaVM *(JNICALL * GetJavaVM) (VMInterface * vmi);
HyPortLibrary *(JNICALL * GetPortLibrary) (VMInterface * vmi);
HyVMLSFunctionTable *(JNICALL * GetVMLSFunctions) (VMInterface * vmi);
HyZipCachePool *(JNICALL * GetZipCachePool) (VMInterface * vmi);
JavaVMInitArgs *(JNICALL * GetInitArgs) (VMInterface * vmi);
vmiError (JNICALL * GetSystemProperty) (VMInterface * vmi, char *key,
char **valuePtr);
vmiError (JNICALL * SetSystemProperty) (VMInterface * vmi, char *key,
char *value);
vmiError (JNICALL * CountSystemProperties) (VMInterface * vmi,
int *countPtr);
vmiError (JNICALL * IterateSystemProperties) (VMInterface * vmi,
vmiSystemPropertyIterator
iterator, void *userData);
};

VM Interface 和 JNI、JVMTI 一样,是一个函数指针表,根据 Harmony 的规范,只要 Java 虚拟机的撰写者提供 VMInterfaceFunctions_ 中的前五个函数,就可以与 Harmony 框架兼容。 Java 虚拟机的提供者可以自由选择是否给 VM Interface 中添加更多的函数以强化其功能。从定义中可以看到,IBM 9J 虚拟机也提供了一系列访问系统变量的函数。

VM Interface 最重要的功能是沟通 Harmony 各主要模块,接下来就讨论一下如何利用 VM Interface 在类库,虚拟机以及 Port Layer 之间转换。

类库本地代码中可能遇到的三个指针: JavaVM 指针、JNIEnv 指针、jvmtiEnv 指针。

它们之间的转换包括:

(1) 从 JavaVM 到 JNIEnv:


struct HyPortLibrary
				
(*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_2);

(2) 从 JavaVM 到 jvmtiEnv:


struct HyPortLibrary
				
(*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_0);

(3) 从 JNIEnv 到 JavaVM:


struct HyPortLibrary
				
(*env)->GetJavaVM(env, &vm);

从类库本地代码也可以获得 VM Interface:

函 数 JNICALL VMI_GetVMIFromJavaVM(JavaVM * vm ) 和 VMInterface* JNICALL VMI_GetVMIFromJNIEnv(JNIEnv * env) 分别能通过类库本地的 JavaVM 或者 JNIEnv 参数获得 VM Interface。然而这两个函数又是从哪里来的呢?记得吗,我们曾经提到,VM 的撰写者必须以某种方式将从他的 VM Interface 提供给类库。是的,这两个函数就是起到这样作用的,它们是从 VM 的共享库中导出的。

从 VM Interface 到 Port Layer,有 GetPortLibrary 函数。在 Harmony 体系设计中,Port Layer 是与 Java VM 紧密联系的。例如,每个 J9 VM 启动后都会自动生成一个相关联的 Harmony Port Library。Harmony 架构中的其他部分,如类库,要使用 Port Layer 的话,都要通过 VM Interface 的。事实上,在本地代码中经常使用的宏 PORT_ACCESS_FROM_ENV () 就是先将 JNIEnv 转换成 VM Interface 然后调用 GetPortLibrary 获得 Port Layer 的。


Kernel Class

Kernel Class 是一些与 Java 虚拟机紧密联系而又为 Java 虚拟机所知的一系列 Java 类。这些 Kernel Class 一般是同 Java 虚拟机一起提供的,IBM J9 虚拟机也有配套的 Kernel Class,这些类是开源的,其他的 VM 作者可以参考这些 Kernel Class,但是如果想直接迁移这些类,VM 的开发人员就必须依据各自 VM 的功能实现 Kernel Class 的本地调用。

Kernel Class 包括以下这些类:


Kernel Classes
				
? java.lang.Object
? java.lang.Class
? java.lang.ClassLoader
? java.lang.Compiler
? java.lang.Package
? java.lang.Runtime
? java.lang.Thread
? java.lang.reflect.AccessibleObject
o java.lang.reflect.Constructor
o java.lang.reflect.Field
o java.lang.reflect.Method
? java.lang.reflect.Array
? java.lang.ref.Reference

此外,Kernel Class 还包括一些 org.apache.harmony 前缀下的实现类,值得一提的是 org.apache.harmony.kernel.vm.VM。其功能包括取出各种类装载器,如

      static public final ClassLoader getNonBootstrapClassLoader();

还包括一些控制虚拟机行为的工具,如

        public static void addShutdownHook(Thread hook);

将在虚拟机关闭时执行钩子程序 hook。有意思的是,File.deleteOnExit() 也是通过 org.apache.harmony.kernel.vm.VM 实现的,读者有兴趣的话,可以在 Harmony 源码中找一下调用链。

Harmony 类库中包括 luni-kernel-stub.jar,那么 Kernel Class 和这个 luni-kernel-stub.jar 有什么关系?Kernel Class 究竟是在 VM 中实现的,还是在类库实现的呢?

事 实上,luni-kernel-stub.jar 它只是一个存根。简单的说,这个 luni-kernel 模块(包括相应的 luni-kernel-stub.jar)只是在编译过程中起作用,其中包括的类具有所有必须的方法和属性,实现也符合 Java 语法,但是,仅此而已,至于其功能则无关紧要。这些存根类存在的意义只是使得 javac 编译器能够“resolve”代表这些类的符号,确定这些类的方法和属性。实际运行的时候,Java 虚拟机能够认出这些 Kernel Class 的——因为这些类就是 VM 实现的,它不会从 luni-kernel-stubs.jar 装入这些存根类,而是装入真正的实现类。任何 Harmony 兼容虚拟机都必须要实现这些真正的实现类


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


Java Native Interface

Java Native Interface (JNI) 是 Java 程序和本地代码(主要是 c 代码)间的接口,它的存在,使在 C 代码层操作 Java 对象成为可能。更重要的是,它提供了一个 Java 代码使用本地代码的方法,使许多在 Java 上还不能实现的,或者有效率相关的功能,可以使用 JNI 来调用本地代码来实现,大大地提高了 Java 的扩展性能。但是,要注意的是,JNI 所调用的本地代码往往是不具有跨平台能力的,这就要开发者使用别的手段,保证自己程序的跨平台能力。这一点,我们在上一篇(研究 port layer)中有提到了。

JNI 的数据类型

JNI 首先定义了 Java 的数据类型,很简单,就是在 int,long 等标识符上加上“j”,就是 jint、jlong,这些数据类型是和 Java 中的数据类型 一一对应的,而且也是平台无关的。Java 的对象、对象内部的域、成员函数等等,JNI 都有自己的数据结构,如对象的成员函数,就可以用 jmethod 来指向,而 jfield 就指向对象的域。

使用 JNI 对象操作

JNI 的存在使 C 代码对 Java 对象的操作大大简化了(虽然与 Java 本身相比还是比较复杂)。JNI 函数提供了对 Java 中类、对象、域、函数的操作。C 代码 可以通过反射获得一个类的句柄,对它内部的域进行操作,调用它的函数,等等,并且这些操作可以针对类内部的私有变量和私有函数,这使得 JNI 非常强大,也要求用户 使用 JNI 时一定要自己关心安全问题。

Java 中的 String 和数组都是具有 Java 特色的,JNI 也提供了对他们的特殊支持。Java 1.4 有了 NIO 模块,JNI 也专门提供支持 NIO DirectBuffer 操作的函数。

JNI 还可以操作 Java 中的异常,它可以在 C 代码层就扔出异常,也可以取消当前所存在的异常,这使 C 代码有了本身所不具有的异常处理功能。

不仅如此,JNI 还提供了对 Java VM 的操作手段,在 C 代码中,用户便可以自如地创建和清除 JVM。

总而言之,JNI 使 C 代码和 Java 代码第一次可以无缝地融合在一起,C 代码可以自由地使用 Java 对象,Java 也可以使用 C 代码中强大的功能。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


JNI 与 Harmony 开发

在 Harmony 的开发过程中,JNI 的意义是非常大的。Java 语言本身并没有对本地资源(文件,网络等等)的操作能力,VM 也没有提供这些功能。这些都需要开发者自己 来实现。为了实现这些基本的功能,就必须从 Java 层通过 JNI 做系统调用。Harmony 现在的许多模块,像 java.io,java.net,java.net 等等类库,都是大量地使用了这种方法来调用本地代码。除此之外,许多效率相关的代码,也是采用了这种手段。

下面我们就用一个网络 Socket 的简单例子来说明。

功能定义

我们需要一个基本的 TCP socket 来读写网络资源,假设我们不能使用 Java 中的 java.net 和 java.nio.channels 类库(他们还没有实现!),我们就可以使用 JNI 来调用底层系统 API 来完成这个功能。

接口定义

在上一篇文章我们就已经提到,要从 Java 层调用到底层 c 代码,首先在 java code 中定义一个接口,使用 native 关键字,比如:

        public native long create_socket();
public native long read_socket(byte[] buf);
public native long write_socket(byte[] buf);

这几个个函数的实现将在底层 native code 中实现,在 Java层,我们可以直接调用该函数,就好像调用一个普通的 Java 函数那样。

然后就是使用 javah 来生成一个 .h 文件,里面包含了该函数的原型。这些都在我们的上一篇文章上提到过。

使用 JNI 的数据定义

要操作 Java 对象,要使用 JNI 所定义的平台无关的数据结构,比如:

        jclass  // java class 定义类型
jobject // java 对象类型
jfieldID // java 对象中域类型
jmethodID // java 函数类型

还有 jint、jlong、jboolean 等等 Java 中所谓原始类型。此外,程序可以定义自己的数据,但为了扩展性考虑,最好使用一些能跨平台的数据类型和算法。

使用 JNI 函数操作 Java 对象

在 C 语言中使用 JNI 操作 Java 对象的时候,C 编译器并不会严格地检查类的类型,方法参数等等,这些都需要非常小心,一旦有一点差错,都会使程序崩溃。

在 JNI 中,每一个 Java 对象都可以由一个 jobject 的指针代表。操作一个对象的数据域和方法,可以先取得其 class 定义对象,再找到它在 class 中的特定 ID,再用 JNI 函数进行操作。

比如,我们在函数中要操作 Socket 类中的 port 域,就应该:

        // 我们已经有了 JNIEnv 指针 env 和 socket 对象指针 obj_socket
// 先取得 socket 类的定义
jclass clz_socket = (*env)->findClass(env,"java.net.Socket");
// 取得 socket 类中 port 的 ID
jfieldID f_port = (*env)->GetFieldID(env,clz_socket,"port","I");
// 拿到 port 值
jint port = (*env)->GetIntField(env,obj_socket,f_port);

然后我们就可以作一些别的操作。如果要重新设定 port 的值为 new_value,我们还可以使用

        (*env)->GetIntField(env,obj_socket,new_value);

来完成这个任务。

错误处理

JNI 函数中出错,往往不能 exit 了之,我们要记得我们事实上在写 Java 程序,要遵守 Java 的一些概念和通常做法。我们可以把错误信息(错误代码)返回给 Java 代码进行处理,也可以通过扔一个异常来通知上层这个错误。当然,如果发生了十分严重的错误,JNI 还提供了一个 FatalError 函数来通知 VM 发生了致命错误。

函数体实现

函数实际逻辑的实现和一般的c程序并没有大的区别。比如我们要实现一个 socket 的连接,并把它的句柄保存在 socket 对象的域 long sock 中:


				
//定义数据类型
jbyte nAddrBytes[HYSOCK_INADDR6_LEN];
int length;
U_16 nPort;
I_32 result;
...
//我们已经获得了一个 socket 资源,先检查其是否有效
if (!socketIsValid (socket)){
// 无效资源,抛出异常
(*env)->throwNew(env,findClass("java/net/SocketException"),"BadSocket");
return -1;
}
else{
jclass clz_socket = (*env)->findClass(env,"java.net.Socket");
// 取得socket类中port的ID
jfieldID f_port = (*env)->GetFieldID(env,clz_socket,"port","I");
// 拿到port值
jint remotePort = (*env)->GetIntField(env,obj_socket,f_port);

// 设定端口和地址
nPort = htons ((U_16) remotePort);
sockaddrP = setAddr(&sockaddrP, (U_8 *) nAddrBytes, length,
HYADDR_FAMILY_AFINET4, nPort, 0, socketP);

// 连接
result = connect (socketP, &sockaddrP);
// 判断是否成功
if (0 != result)
{
//失败则抛出异常
(*env)->throwNew(env,findClass("java/net/ConnectException"),NULL);
return -1;
}
}
return result;

基本上流程与其他程序没有太大的区别,但是要考虑到与上层 Java 代码的衔接和 Java 异常处理机制。

资源释放

与 Java 代码不同,在 JNI 中显式申请的内存空间并不会被垃圾处理器所收集,网络和文件资源也不会自动释放,所以在完成所有的任务之后,我们应该显式地释放所有申请的资源。

编译运行

在完成代码编写之后,我们可以把这些代码编译后与其他类库链接成动态链接库,并在 Java 层用 System.loadLibrary(filename) 的方式载入。在 Harmony 中,Harmony 兼容的虚拟机将完成载入工作。

调试

不 能否认,要对使用 JNI 的生成的程序作调试是比较困难的,上层是 JAVA,下面是 C 代码,我们可以用开发平台在 Java 层调试 Java 代码,但一旦调用 到 C 代码层,这些 Java 开发工具就无能为力了。同样,我们可以用 C 的开发平台调试 C 代码,但是却不能得到当时确切的 Java 环境变量。这个时候,就往往 需要一些特殊的手段,比如得到运行到某段本地代码前的环境,然后再在 C 开发环境里测试;或者对比程序中特定位置的输入输出来查找代码所在的位置:这个时候往往要求开发人员有一定的经验和对程序的良好感觉。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


JVM Tools Interface

JVMTI 是 Java 1.5 虚拟机提供的一套全新的本地编程接口。通过这一套接口,开发者可以创建软件代理观察运行在虚拟机内部的应用,同时可以控制这些应用的运行。通过这一接口, 开发人员可以实现一整套监控虚拟机的工具,包括调试工具,监控工具,线程分析工具,和代码覆盖率分析工具等等。这套本地接口取代了早期虚拟机的 JVMPI (Java virtual machine profiling interface) 接口和 JVMDI(Java virtual machine debug interface)接口。原来的 profiling 接口存在几个问题。首先这个接口是实验性质的,并且已经被弃用。其次,profiling 接口不便于使用,使用其开发的代理很不稳定。

在 Harmony 中,所有兼容的虚拟机都必须实现 JVMTI 接口,作为 Harmony VMI 的一部分。用户自己开发的应用 JVMTI 接口的代码,都可以编译成动态链接库以

        java -agentlib:[=]

的方式载入运行。同时,J2SE 1.5 在 Java 层也提供了 Instrument 包,为用户用 Java 代码开发一些代理程序提供了方便。

使用 JVMTI

在 Harmony 中,JVMTI 和 JNI 类似,通过一个 JVMTI 指针,我们可以使用 JVMTI 所提供的所有函数。这样的设置对用户来说还是非常简单的。

比如,要得到一个当前由 JVM 加载的所有类的数组,我们可以使用:

   int count;
jclass * classes_ptr;
// 返回当前由 JVM 加载的所有类的数组
jvmtiError err = (*jvmti)->GetLoadedClasses(jvmti, &count, &classes_ptr);
if (JVMTI_NONE = err){
//做些事情
}

要注意的是,与 JNI 错误代码相对应,JVMTI 自身定义了许多错误代码。

JVMTI 中的错误处理

使 用 JVMTI 时,一旦发生了一些错误,它的处理可能需要特殊处理。这是因为,使用 JVMTI 往往在底层做一些监测,类的重定义,它一旦发生错误,后果往往是严重的,甚至是不可恢复的(可以设想 Object 类如果被乱改了一通,当然现在这个不被 VM 所支持),因此对于这些错误,需要立即扔出一个 Exception,或者立即通知 VM 发生了不可恢复的错误(比如用 JNI 的 FatalError 等)。当然,此时处理本地所动态申请的内存是必要的。

Instrument 包

JVMTI 接口是本地接口。熟悉本地接口的开发人员都知道使用本地接口进行开发的缺陷。首先,本地接口造成了移植性的问题,其次由于本地接口可以和操作系统直接交 户,在提供了灵活性的同时增加了开发的难度并且会引进缺陷(bug)。于是在新版本(Java 1.5)的类库中,增加了一个包:java.lang.Instrumentation。这个包封装了部分 JVMTI 的接口,使程序员可以在 Java 代理应用程序中,对由虚拟机载入进来的类的字节码进行安全而方便的修改,以达到监测运行在 JVM 上的应用程序的目标。

Instrument 包主要提供了几个 interface,开发人员只要继承实现了这些 interface,并在运行的时候指定 premain 函数所在的类,就可以实现自己的代理。

其中,最关键是两个功能:一是类的监听转化(Tranform),一个是类的重定义(Redefine)。

类 的重定义,是指对已有的类在字节码(byte code)层次上的替换。比如,我们想把当前类库里的某一个类进行替换,改变它的某个函数,就可以使用这一个功能。这个重定义是动态的,这使得 Java 代码的监控和调试变得简单。当然,这个功能在现在还有很多限制,比如,重定义后的类的域的类型、方法的参数的返回值,必须和初始类一致,也不能减少和增 加。甚至有些类是不可重定义的(所以我们上面所举的重定义 Object 类的例子,现在还是不可能的)。

对类的监听转化主要是 继承实现 Instrument 包的 ClassFileTransformer(其实就是实现它的transform方法),并添加到 Transformer 队列里面,这样,在任何类的定义和重定义的时候,虚拟机都会调用到这个方法,用户就可以在这个时候做一些监控工作。

Harmony 缺省有一个继承 Instrument 接口的实现类,用户在使用

          java -javaagent:[=]

就可以访问到这个类。它提供了查看当前所有类的定义,添加和删除 ClassFileTransformer 等等功能,当然,这些功能最终是由底层的 JVMTI 提供的。

Harmony Java 代理的启动

上 面提到,Intrument 包提供了用 Java 代码开发代理应用程序一种方式,那么,它是怎样与底层结合的呢?Java 代理(javaagent)和动态链接库代理(libagent)有怎么样的联系呢?在 Harmony 中,事实上,我们是以动态链接库方式提供了一个基础的缺省实现,使用户不必自己再去写一个底层动态链接库来启动自己的代理,从而实现了动态链接库代理到 Java 代理的转换。

从和动态链接库代理到 Java 代理,其中间的桥梁就是 Harmony 自己实现的缺省的 Instrument 库。这个过程可以简单地表示为:Harmony 首先读取自己的缺省的 instrument 库,再通过分析命令行参数中取得了 jarpath,读取这个 jar 文件并且分析它的 MANIFEST,然后就可以读入用户含有 premain 函数的类,最后执行 premain 函数,同时传给它一个默认 Instrument 接口的实现类的对象。

但是,在这个过程中,要注意的是多数类没有被读进来(事实上,已经存在的仅是几个核心类),我们要做的事情,比如读取 jar 文件,分析它的 MANIFEST 等等,都还不能调用已有的类库,而需要自己做一些工作。


blue_rule.gif
c.gif
c.gif
u_bold.gif回页首


结语

Harmony VMI 是 Harmony 项目的重要部分,它为 Java 虚拟机和类库代码之间提供了大量标准 API 调用。Harmony VMI 不仅有 J2SE 标准的 JNI,JVMTI,更有自己特殊的核心类接口。

通过使用 Harmony VMI,Harmony 项目分离了它的类库和虚拟机的实现,所有想和 Harmony 兼容的 VM,仅仅需要符合这一个 VMI 定义就可以了。这样,不同的 VM 可以使用同一套 Harmony 类库,大大提高了可扩展性。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-130289/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/374079/viewspace-130289/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值