Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7191147
  • 博文数量: 510
  • 博客积分: 12019
  • 博客等级: 上将
  • 技术积分: 6836
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-01 16:46
文章分类

全部博文(510)

文章存档

2022年(2)

2021年(6)

2020年(59)

2019年(4)

2018年(10)

2017年(5)

2016年(2)

2015年(4)

2014年(4)

2013年(16)

2012年(47)

2011年(65)

2010年(46)

2009年(34)

2008年(52)

2007年(52)

2006年(80)

2005年(22)

分类: C/C++

2007-10-31 10:51:39

封装 JNI 控制 JVM [1] 配置并构造 JVM

     前两天有位朋友问到在 .NET 里面调用 Java 类方法并返回值的方法,刚好手头工作可能会需要用 JNI 来封装现有系统,于是晚上写了个调用例 子,顺手对 JNI 的基本调用做了一个简单的封装。因为 JNI 的接口设计上主要面向 C++ 语言,因此决定将 JNI 的封装放在一个独立的  DLL 中进行,然后在 .NET 中通过 Interop 再做一层封装,反正这种需求下效率也不是首要考虑因素。
     JNI 的原理和基本使用方法这里就不多说了,现成的资料太多了,有兴趣的朋友可以参考 sun 的 JNI 以及示例。此外 Sheng Liang 的  一书中对 JNI 做了较为全面的解析。
     JNI 的封装其实现成的有很多,如 sourceforge.net 上的  等; Java 和 .NET 互操作的封装也有一些,如  等。但一方面自己的需求与他们不太一样,另一方面也希望通过自行开发增加对这方面知识的了解,故而选择自己重新发明轮子,呵呵,权且当作是一种娱乐吧 :P

 [1] 配置并构造 JVM

     JVM 的配置与构造与 CLR 的非常类似(CLR 的相关使用方法可以参考《.Net平台下CLR程序载入原理分析 [草稿]》一 文 )。用户程序通过 JRE 的 jvm.dll 提供的接口,构造并配置虚拟机,调用相应的类的方法,完成实际工作。从 JDK 1.2 开始, JRE 提供了 client/server 两个版本的 jvm.dll,一般可以在 %JRE%\bin\client 和 %JRE%\bin\ server 目录下找到,和 CLR 中的 wks/srv 类似。jvm.dll 提供了 JNI_CreateJavaVM 函数用于构造虚拟机, 对应于 CLR 中 mscoree.dll 提供的 CorBindToRuntimeEx 函数。此函数的定义可以在 %JDK%\include\ jni.h 头文件里面找到,静态链接可以通过 %JDK%\lib\jvm.lib 库文件完成,动态连接则可以根据选用 JRE 的虚拟机类型从相应  jvm.dll 中使用 GetProcAddress 函数获得入口。
 

以下为引用:

 _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
 


     pvm 参数返回虚拟机的对象指针,penv 参数返回当前线程执行环境的对象指针,args 参数传入虚拟机构造时使用的配置参数。
      一般来说,每个进程同时只能启动一个 JVM 实例,这在很多 JVM 实现中是硬编码在虚拟机构造代码中。因此使用者应该通过 singleton 模 式保障全局 JVM 实例的唯一性,或者通过 JNI_GetCreatedJavaVMs 函数获取已经构造的虚拟机对象指针。而 JavaVM:: DestroyJavaVM 方法,大多数情况下是不起作用的,只是需要在程序结束是礼节性的调用一下,不要指望通过它真正卸载 JVM,否则再次调用  JNI_CreateJavaVM 函数时可能会发生错误。一个简单的封装如下:
 
以下为引用:

 namespace JBridge
 {
  public class JStub
  {
     [DllImport("JvmStub.dll")]
     public static extern IntPtr CreateJVM();

     [DllImport("JvmStub.dll")]
     public static extern void DestroyJVM(IntPtr vm);
   }
   
   public class JVM 
   {
     static private IntPtr _vm = IntPtr.Zero;    

     private JVM()
     {
     }

     ~JVM()
     {
       if(_vm != IntPtr.Zero)
       {
         JStub.DestroyJVM(_vm);
         _vm = IntPtr.Zero;
       }      
     }

     public static IntPtr getInstance()
     {
       if(_vm == IntPtr.Zero)
       {
         lock(typeof(JVM))
         {
           if(_vm == IntPtr.Zero)
           {
             _vm = JStub.CreateJVM();
           }
         }
       }
       return _vm;
     }
   }
 }
 


    
      而在一个创建了 JVM 的进程中,并不是所有线程都能够直接使用 JVM 的,需要通过 JavaVM::AttachCurrentThread 方 法将当前线程挂接到 JVM 环境上,并获取当前线程执行环境的对象指针 JNIEnv *pEnv;在无需处理 JVM 调用时,应该调用  JavaVM::DetachCurrentThread 方法析构 JVM 为当前线程准备的数据;而 JNI_CreateJavaVM 函数在构 造 JVM 时缺省返回调用线程的执行环境对象指针。
 
以下为引用:

 struct JavaVM_
 {
   jint DestroyJavaVM()
   {
     // ...
   }
   jint AttachCurrentThread(void **penv, void *args)
   {
     // ...
   }
   jint DetachCurrentThread()
   {
     // ...
   }
 }
 


     最后的 args 参数是配置虚拟机以何种方式启动。
 
以下为引用:

 #define JNI_VERSION_1_1 0x00010001
 #define JNI_VERSION_1_2 0x00010002
 #define JNI_VERSION_1_4 0x00010004

 typedef struct JavaVMInitArgs {
   jint version;
   jint nOptions;
   JavaVMOption *options;
   jboolean ignoreUnrecognized;
 } JavaVMInitArgs;
 



     字段 version 指定虚拟机使用的兼容版本,如 jni.h 定义的 JNI_VERSION_1_2;
     字段 nOptions 和 options 指定虚拟机使用哪些配置。
 
以下为引用:

 typedef struct JavaVMOption {
   char *optionString;
   void *extraInfo;
 } JavaVMOption;
 


      配置信息以字符串形式保存在 JavaVMOption::optionString 中,由 JVM 进行具体解析。常用的配置有诸如显示 JNI 调 试信息(-verbose:jni);对 JNI 调用进行额外检查(-Xcheck:jni);以及指定 Java 类(- Djava.class.path=???)、JNI 本地库(-Djava.library.path=???)、初始化类库(- Xbootclasspath:???)等等路径。
     一个实际的配置和构造 JVM 示例代码如下:
 以下为引用:

 JavaVMOption opts[5];

 opts[0].optionString = "-Djava.class.path=F:\\Study\\Java\\JNI\\Prompt";
 opts[1].optionString = "-Djava.library.path=F:\\Study\\Java\\JNI\\Prompt";
 opts[2].optionString = "-verbose:jni";
 opts[3].optionString = "-Xcheck:jni";
 opts[4].optionString = "-Xbootclasspath:E:\\Borland\\JBuilderX\\jdk1.4\\jre\\lib\\rt.jar";

 JavaVMInitArgs args;

 args.version = JNI_VERSION_1_2;
 args.options = opts;
 args.nOptions = sizeof(opts) / sizeof(opts[0]);
 args.ignoreUnrecognized = JNI_TRUE;

 Check(JNI_CreateJavaVM(&m_jvm, reinterpret_cast(static_cast(m_env)), &args));
 








/////////////////////
阅读(3134) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~