通过对Lua语言和C/C++语言的混合时候,对于语言间混合使用有了初步的了解.
但是Lua和C/C++语言交换的原理是具有GC垃圾收集器来管理内存的语言和手动内存管理的典型代表.
同样Java语言与C/C++/Object-c语言交换也存在这些问题.在java中使用JNI技术来解决这个问题.
这些问题涉及到如下方面:
1 - java动态语言中的基本类型,Object类以及自定类如何与C/C++/Object-c的语言类型进行映射.???
2 - java动态语言如何调用C/C++/Object-c 中方法.???
3- C/C++/Object-c语言如何调用java语言中的方法.???
4- C/C++/Object-c 与java语言中堆内存产生的内存如何管理???
Lua语言通过ANSI C语言实现,本身提供很少语言级API. 但是java作为一种强大的通用型语言,提供网络,文件,数据库等各种强大的库,同时,java作为一种跨平台通用型语言必须解决平台间的差异,
所以,java jni技术相对lua 与C/C++交换的复杂程度不在同一个数量基本. lua与c/c++仅仅需要精通c/c++语言,而java与c/++同时要精通两种庞大的语言,所以复杂行可想而知.
在这里我们仅仅作为了解来看JNI技术.
首先在jni.h文件中头文件中罗列了java语言与C/C++/Object-c语言相互交换所涉及的所有东西.在Lua与C/C++中涉及的这一部分仅仅这这里罗列的不及百分之一吧.
上代码:
jni.h 文件:
/*
* We used part of Netscape's Java Runtime Interface (JRI) as the starting point of our design and implementation.
*/
//在JDK7 -windows -64bit下是通过JRI来设计和实现JNI.
/******************************************************************************
* Java Runtime Interface
* Copyright (c) 1996 Netscape Communications Corporation. All rights reserved.
*****************************************************************************/
#ifndef _JAVASOFT_JNI_H_
#define _JAVASOFT_JNI_H_
#include <stdio.h>
#include <stdarg.h>
/* jni_md.h contains the machine-dependent typedefs for jbyte, jint and jlong */
/*jni_md.h为jni.h的补充文件,用来根据平台不同来定义 jbtye,jint 和jlong类型*/
#include "jni_md.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* JNI Types
*/
#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H
//为跨平台来定义基本的java原始类型
typedef unsigned char jboolean; //布尔类型为java基本类型,底层char类型来实现.可能因为c语言在c99版本才增加布尔类型导致的.
typedef unsigned short jchar;
typedef short jshort;
typedef float jfloat; //浮点类型1
typedef double jdouble; //浮点类型2
typedef jint jsize;
#ifdef __cplusplus
//定义java基础类
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
#endif
typedef jobject jweak;
//java作为一种通用的脚本语言定义的值类型
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
struct _jfieldID;
typedef struct _jfieldID *jfieldID;
struct _jmethodID;
typedef struct _jmethodID *jmethodID;
/* Return values from jobjectRefType */
typedef enum _jobjectType {
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
} jobjectRefType;
#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */
/*
* jboolean constants 布尔类型常量
*/
#define JNI_FALSE 0
#define JNI_TRUE 1
/*
* possible return values for JNI functions. Java调用C/C++函数的返回值
*/
#define JNI_OK 0 /* success */
#define JNI_ERR (-1) /* unknown error */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error */
#define JNI_ENOMEM (-4) /* not enough memory */
#define JNI_EEXIST (-5) /* VM already created */
#define JNI_EINVAL (-6) /* invalid arguments */
/*
* used in ReleaseScalarArrayElements
*/
#define JNI_COMMIT 1
#define JNI_ABORT 2
/*
* used in RegisterNatives to describe native method name, signature,
* and function pointer.
*/
/* 这个类似于Lua中的函数签名,为java调用c/c++函数定义统一接口*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
/*
* JNI Native Method Interface.
*/
struct JNINativeInterface_;
struct JNIEnv_;
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
/*
* JNI Invocation Interface.
*/
struct JNIInvokeInterface_;
struct JavaVM_;
#ifdef __cplusplus
typedef JavaVM_ JavaVM;
#else
typedef const struct JNIInvokeInterface_ *JavaVM;
#endif
struct JNINativeInterface_ {
jclass (JNICALL *DefineClass)
(JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
jsize len);
jclass (JNICALL *FindClass)
(JNIEnv *env, const char *name);
jstring (JNICALL *NewString)
(JNIEnv *env, const jchar *unicode, jsize len);
jsize (JNICALL *GetStringLength)
(JNIEnv *env, jstring str);
const jchar *(JNICALL *GetStringChars)
(JNIEnv *env, jstring str, jboolean *isCopy);
void (JNICALL *ReleaseStringChars)
(JNIEnv *env, jstring str, const jchar *chars);
jweak (JNICALL *NewWeakGlobalRef)
(JNIEnv *env, jobject obj);
void (JNICALL *DeleteWeakGlobalRef)
(JNIEnv *env, jweak ref);
jboolean (JNICALL *ExceptionCheck)
(JNIEnv *env);
//这里应该是JDK中ByteBuffer对应的函数
jobject (JNICALL *NewDirectByteBuffer)
(JNIEnv* env, void* address, jlong capacity);
void* (JNICALL *GetDirectBufferAddress)
(JNIEnv* env, jobject buf);
jlong (JNICALL *GetDirectBufferCapacity)
(JNIEnv* env, jobject buf);
/* New JNI 1.6 Features */
jobjectRefType (JNICALL *GetObjectRefType)
(JNIEnv* env, jobject obj);
};
/*
* We use inlined functions for C++ so that programmers can write:
*
* env->FindClass("java/lang/String")
*
* in C++ rather than:
*
* (*env)->FindClass(env, "java/lang/String")
*
* in C.
*/
//定义C/C++可以调用java函数的基本接口
struct JNIEnv_ {
const struct JNINativeInterface_ *functions;
#ifdef __cplusplus
jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
jsize len) {
return functions->DefineClass(this, name, loader, buf, len);
}
jclass FindClass(const char *name) {
return functions->FindClass(this, name);
}
jobject AllocObject(jclass clazz) {
return functions->AllocObject(this,clazz);
}
jobject NewObject(jclass clazz, jmethodID methodID, ...) {
va_list args;
jobject result;
va_start(args, methodID);
result = functions->NewObjectV(this,clazz,methodID,args);
va_end(args);
return result;
}
jobject NewObjectV(jclass clazz, jmethodID methodID,
va_list args) {
return functions->NewObjectV(this,clazz,methodID,args);
}
jobject NewObjectA(jclass clazz, jmethodID methodID,
const jvalue *args) {
return functions->NewObjectA(this,clazz,methodID,args);
}
jclass GetObjectClass(jobject obj) {
return functions->GetObjectClass(this,obj);
}
jboolean IsInstanceOf(jobject obj, jclass clazz) {
return functions->IsInstanceOf(this,obj,clazz);
}
jstring NewString(const jchar *unicode, jsize len) {
return functions->NewString(this,unicode,len);
}
jsize GetStringLength(jstring str) {
return functions->GetStringLength(this,str);
}
const jchar *GetStringChars(jstring str, jboolean *isCopy) {
return functions->GetStringChars(this,str,isCopy);
}
void ReleaseStringChars(jstring str, const jchar *chars) {
functions->ReleaseStringChars(this,str,chars);
}
jstring NewStringUTF(const char *utf) {
return functions->NewStringUTF(this,utf);
}
jsize GetStringUTFLength(jstring str) {
return functions->GetStringUTFLength(this,str);
}
const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
return functions->GetStringUTFChars(this,str,isCopy);
}
void ReleaseStringUTFChars(jstring str, const char* chars) {
functions->ReleaseStringUTFChars(this,str,chars);
}
jsize GetArrayLength(jarray array) {
return functions->GetArrayLength(this,array);
}
jobjectArray NewObjectArray(jsize len, jclass clazz,
jobject init) {
return functions->NewObjectArray(this,len,clazz,init);
}
jobject GetObjectArrayElement(jobjectArray array, jsize index) {
return functions->GetObjectArrayElement(this,array,index);
}
void SetObjectArrayElement(jobjectArray array, jsize index,
jobject val) {
functions->SetObjectArrayElement(this,array,index,val);
}
jbooleanArray NewBooleanArray(jsize len) {
return functions->NewBooleanArray(this,len);
}
jbyteArray NewByteArray(jsize len) {
return functions->NewByteArray(this,len);
}
jcharArray NewCharArray(jsize len) {
return functions->NewCharArray(this,len);
}
jshortArray NewShortArray(jsize len) {
return functions->NewShortArray(this,len);
}
jintArray NewIntArray(jsize len) {
return functions->NewIntArray(this,len);
}
jlongArray NewLongArray(jsize len) {
return functions->NewLongArray(this,len);
}
jfloatArray NewFloatArray(jsize len) {
return functions->NewFloatArray(this,len);
}
jdoubleArray NewDoubleArray(jsize len) {
return functions->NewDoubleArray(this,len);
}
jweak NewWeakGlobalRef(jobject obj) {
return functions->NewWeakGlobalRef(this,obj);
}
void DeleteWeakGlobalRef(jweak ref) {
functions->DeleteWeakGlobalRef(this,ref);
}
jobject NewDirectByteBuffer(void* address, jlong capacity) {
return functions->NewDirectByteBuffer(this, address, capacity);
}
void* GetDirectBufferAddress(jobject buf) {
return functions->GetDirectBufferAddress(this, buf);
}
jlong GetDirectBufferCapacity(jobject buf) {
return functions->GetDirectBufferCapacity(this, buf);
}
jobjectRefType GetObjectRefType(jobject obj) {
return functions->GetObjectRefType(this, obj);
}
#endif /* __cplusplus */
};
typedef struct JavaVMOption {
char *optionString;
void *extraInfo;
} JavaVMOption;
typedef struct JavaVMInitArgs {
jint version;
jint nOptions;
JavaVMOption *options;
jboolean ignoreUnrecognized;
} JavaVMInitArgs;
typedef struct JavaVMAttachArgs {
jint version;
char *name;
jobject group;
} JavaVMAttachArgs;
/* These will be VM-specific. */
#define JDK1_2
#define JDK1_4
/* End VM-specific. */
struct JNIInvokeInterface_ {
void *reserved0;
void *reserved1;
void *reserved2;
jint (JNICALL *DestroyJavaVM)(JavaVM *vm);
jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args);
jint (JNICALL *DetachCurrentThread)(JavaVM *vm);
jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);
jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);
};
struct JavaVM_ {
const struct JNIInvokeInterface_ *functions;
#ifdef __cplusplus
jint DestroyJavaVM() {
return functions->DestroyJavaVM(this);
}
jint AttachCurrentThread(void **penv, void *args) {
return functions->AttachCurrentThread(this, penv, args);
}
jint DetachCurrentThread() {
return functions->DetachCurrentThread(this);
}
jint GetEnv(void **penv, jint version) {
return functions->GetEnv(this, penv, version);
}
jint AttachCurrentThreadAsDaemon(void **penv, void *args) {
return functions->AttachCurrentThreadAsDaemon(this, penv, args);
}
#endif
};
#ifdef _JNI_IMPLEMENTATION_
#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT
#else
#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT
#endif
_JNI_IMPORT_OR_EXPORT_ jint JNICALL
JNI_GetDefaultJavaVMInitArgs(void *args);
_JNI_IMPORT_OR_EXPORT_ jint JNICALL
JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
_JNI_IMPORT_OR_EXPORT_ jint JNICALL
JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);
/* Defined by native libraries. */
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved);
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved);
//用来定义JDK版本宏定义
#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* !_JAVASOFT_JNI_H_ */
jni_md.h 文件作为jni.h作为跨平台实现的一个补充文件:
#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall
//在window 64bit 下的jni_md.h文件实现
typedef long jint;
typedef __int64 jlong; //对于int64的支持
typedef signed char jbyte;//对字节类型的支持
#endif /* !_JAVASOFT_JNI_MD_H_ */
在如下资料摘选自Java oracle 官网关于JNI技术的节选. 在资料中主要解决了语言间交换的通用问题.
Java Native Interface Overview
为什么使用JNI???
While you can write applications entirely in Java, there are situations where Java alone does not meet the needs of your application.
Programmers use the JNI to write Java native methods to handle those situations when an application cannot be written entirely in Java.
The following examples illustrate when you need to use Java native methods:
The standard Java class library does not support the platform-dependent features needed by the application.
You already have a library written in another language, and wish to make it accessible to Java code through the JNI.
You want to implement a small portion of time-critical code in a lower-level language such as assembly.
JNI可以做什么???
By programming through the JNI, you can use native methods to:
Create, inspect, and update Java objects (including arrays and strings).
Call Java methods.
Catch and throw exceptions.
Load classes and obtain class information.
Perform runtime type checking.
You can also use the JNI with the Invocation API to enable an arbitrary native application to embed the Java VM.
This allows programmers to easily make their existing applications Java-enabled without having to link with the VM source code.
如何编译,加载以及链接本地方法??
Compiling, Loading and Linking Native Methods
Since the Java VM is multithreaded, native libraries should also be compiled and linked with multithread aware native compilers.
Native methods are loaded with the System.loadLibrary method. In the following example, the class initialization method loads a platform-specific native library in which the native method f is defined:
package pkg;
class Cls
{
native double f(int i, String s);
static
{
System.loadLibrary(“pkg_Cls”);
}
}
The argument to System.loadLibrary is a library name chosen arbitrarily by the programmer.
The system follows a standard, but platform-specific, approach to convert the library name to a native library name.
For example, a Solaris system converts the name pkg_Cls to libpkg_Cls.so, while a Win32 system converts the same pkg_Cls name to pkg_Cls.dll.
The programmer may use a single library to store all the native methods needed by any number of classes, as long as these classes are to be loaded with the same class loader.
The VM internally maintains a list of loaded native libraries for each class loader. Vendors should choose native library names that minimize the chance of name clashes.
If the underlying operating system does not support dynamic linking, all native methods must be prelinked with the VM. In this case, the VM completes the System.loadLibrary call without actually loading the library.
pkg_Cls 参数是程序员定义的作为链接库名称.系统根据不同操作系统的标准把不同的库加载.
例如:在Solaris操作系统会加载libpkg_Cls.so 库,在windows操作系统中会加载pkg_Cls.dll库
程序员可以把所有的本地方法放在同一个动态库中统一加载.
The programmer can also call the JNI function RegisterNatives() to register the native methods associated with a class. The RegisterNatives() function is particularly useful with statically linked functions.
Resolving Native Method Names
Dynamic linkers resolve entries based on their names. A native method name is concatenated from the following components:
the prefix Java_
a mangled fully-qualified class name
an underscore (“_”) separator
a mangled method name
for overloaded native methods, two underscores (“__”) followed by the mangled argument signature
程序员可以调用RegisterNatives()方法注册本地方法. RegisterNatives() 对于静态链接函数非常重要.
解决动态链接库中方法名称 .以java_类全称_方法名称 如果存在重载,则使用参数来区别不同的方法.
The VM checks for a method name match for methods that reside in the native library.
The VM looks first for the short name; that is, the name without the argument signature.
It then looks for the long name, which is the name with the argument signature.
Programmers need to use the long name only when a native method is overloaded with another native method.
However, this is not a problem if the native method has the same name as a nonnative method. A nonnative method (a Java method) does not reside in the native library.
In the following example, the native method g does not have to be linked using the long name because the other method g is not a native method, and thus is not in the native library.
class Cls1 {
int g(int i);
native int g(double d);
}
C语言实现:
The C function with the long mangled name Java_pkg_Cls_f_ILjava_lang_String_2 implements native method f:
在jni.h文件中定义了如何通过C/C++/Object-c 语言来加载java虚拟机到native 应用程序中.
The Invocation API allows software vendors to load the Java VM into an arbitrary native application. Vendors can deliver Java-enabled applications without having to link with the Java VM source code.
在这里提供了VM 对应java虚拟机的封装,类似的在lua中存在lua_status 对lua虚拟机的封装.
Creating the VM
The JNI_CreateJavaVM() function loads and initializes a Java VM and returns a pointer to the JNI interface pointer. The thread that called JNI_CreateJavaVM() is considered to be the main thread.
Attaching to the VM
The JNI interface pointer (JNIEnv) is valid only in the current thread. Should another thread need to access the Java VM, it must first call AttachCurrentThread() to attach itself to the VM and obtain a JNI interface pointer. Once attached to the VM, a native
thread works just like an ordinary Java thread running inside a native method.
Unloading the VM
The JNI_DestroyJavaVM() function unloads a Java VM. As of JDK/JRE 1.1, only the main thread could unload the VM, by calling DestroyJavaVM. As of JDK/JRE 1.2, the restriction was removed, and any thread may call DestroyJavaVM to unload the VM.
如果表格解决了java基本类型 对于c/c++类型中的映射关系.
在java中定义native方法:
public class HelloWorld
{
public native void displayHelloWorld();// java native方法申明
static
{
System.loadLibrary("HelloWorldImpl");// 装入动态链接库,"HelloWorldImpl"是要装入的动态链接库名称。
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
HelloWorld helloWorld = new HelloWorld();
helloWorld.displayHelloWorld();
}
}
通过javac生成的 HelloWorld.class文件中包含一些关键信息.然后通过javah生成HelloWorld.h 文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: displayHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
HelloWorldImpl.cpp 为HelloWorld.h的实现类
#include "HelloWorld.h"
#include <stdio.h>
#include <jni.h>
/*
* Class: HelloWorld
* Method: displayHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *, jobject)
{
printf("Hello World!\n");
return;
}
然后把HelloWorldImpl.cpp 文件编译成HelloWorldImpl.dll 动态链接库.
这里面涉及到Java语言与C/C++语言的协议规定.