??xml version="1.0" encoding="utf-8" standalone="yes"?> www.bitsCN.com YOUR_LIB=your_path for jar in `ls $YOUR_LIB/*.jar`
~译HelloWorldc?br>
2 public native void displayHelloWorld();
3
4 public native void printxx(String str);
5
6 static {
7 System.loadLibrary("hello");
8 // System.load("hello");
9 }
10
11 public static void main(String[] args) {
12 HelloWorld hw = new HelloWorld();
13 hw.displayHelloWorld();
14 for (int i = 0;; ++i) {
15 hw
16 .printxx("wo kaowo kaowo kaowo kaowo kaowo kaowo kaowo kaowo kaowo kaowo kao");
17 if (i % 1000 == 0) {
18 try {
19 Thread.sleep(10);
20 } catch (InterruptedException e) {
21 }
22 }
23 }
24 }
25 }
对编译完的class执行
javah HelloWorld
2 #include <jni.h>
3 /* Header for class HelloWorld */
4
5 #ifndef _Included_HelloWorld
6 #define _Included_HelloWorld
7 #ifdef __cplusplus
8 extern "C" {
9 #endif
10 /*
11 * Class: HelloWorld
12 * Method: displayHelloWorld
13 * Signature: ()V
14 */
15 JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
16 (JNIEnv *, jobject);
17
18 /*
19 * Class: HelloWorld
20 * Method: printxx
21 * Signature: (Ljava/lang/String;)V
22 */
23 JNIEXPORT void JNICALL Java_HelloWorld_printxx
24 (JNIEnv *, jobject, jstring);
25
26 #ifdef __cplusplus
27 }
28 #endif
29 #endif
30
~译
cl -I%java_home%\include -I%java_home%\include\win32 -LDHelloWorld.c -Fehello.dll
2 #include "HelloWorld.h"
3 #include <stdio.h>
4 JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj)
5 {
6 printf("Hello world!\n");
7 return;
8 }
9
10 JNIEXPORT void JNICALL Java_HelloWorld_printxx
11 (JNIEnv *env, jobject obj, jstring prompt){
12
13 const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
14 printf("%s",prompt);
15 (*env)->ReleaseStringUTFChars(env, prompt, str);
16
17 return;
18 }
19
20
3QJavacd和本地类型对?br>
在如下情况下Q需要在本地Ҏ中应用java对象的引用,׃用到cd之间的{换:
1QjavaҎ里面参C入本地方法;
2Q在本地Ҏ里面创徏java对象Q?br>
3Q在本地Ҏ里面returnl果ljavaE序?br>
分ؓ如下两种情况Q?br>
Java原始cd
像booleans、integers、floats{从JavaE序中传到本地方法中的原始类型可以直接用,下面是java中的原始cd和本地方法中的类型的对应Q?br>
Javacd 本地cd 字节(bit)
boolean jboolean 8, unsigned
byte jbyte 8
char jchar 16, unsigned
short jshort 16
int jint 32
long jlong 64
float jfloat 32
double jdouble 64
void void n/a
也就是说如果我在Ҏ中传q去了一个boolean的参数的话,那么我在本地Ҏ中就有jbooleancd与之对应。同理,如果在本地方法中return一个jint的话Q那么在java中就q回一个intcd?中国|管论坛
Java对象
Java对象做ؓ引用被传递到本地Ҏ中,所有这些Java对象的引用都有一个共同的父类型jobject(相当于java中的ObjectcL所有类的父cM?。下面是JNI实现的一些jobject的子c:
4Q本地方法中讉KjavaE序中的内容
1)讉KString对象Q?br>
从javaE序中传q去的String对象在本地方法中对应的是jstringcdQjstringcd和c中的char*不同Q所以如果你直接当做char*使用的话Q就会出错。因此在使用之前需要将jstring转换成ؓc/c++中的char*Q这里用JNIEnv的方法{换。下面是一个例子:
代码3Q?br>
JNIEXPORT jstring JNICALL Java_Prompt_getLine
(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
q里使用GetStringUTFCharsҎ传q来的promptQjstringcdQ{换成为UTFQ?的格式,p够在本地Ҏ中用了?br>
注意Q在使用完你所转换之后的对象之后,需要显C用ReleaseStringUTFCharsҎQ让JVM释放转换成UTF-8的string的对象的I间Q如果不昄的调用的话,JVM中会一直保存该对象Q不会被垃圾回收器回Ӟ因此׃D内存溢出?
下面是访问String的一些方法:
◆GetStringUTFCharsjstring转换成ؓUTF-8格式的char*
◆GetStringCharsjstring转换成ؓUnicode格式的char*
◆ReleaseStringUTFChars释放指向UTF-8格式的char*的指?br>
◆ReleaseStringChars释放指向Unicode格式的char*的指?br>
◆NewStringUTF创徏一个UTF-8格式的String对象
◆NewString创徏一个Unicode格式的String对象
◆GetStringUTFLengt获取UTF-8格式的char*的长?br>
◆GetStringLength获取Unicode格式的char*的长?br>
2) 讉KArray对象Q?br>
和String对象一P在本地方法中不能直接讉Kjarray对象Q而是使用JNIEnv指针指向的一些方法来是用?br>
讉KJava原始cd数组Q?br>
1Q获取数l的长度Q?br>
代码4Q?br>
JNIEXPORT jint JNICALL Java_IntArray_sumArray
(JNIEnv *env, jobject obj, jintArray arr)
{
int i, sum = 0;
jsize len = (*env)->GetArrayLength(env, arr);
如代?所C,q里获取数组的长度和普通的c语言中的获取数组长度不一Pq里使用JNIEvn的一个函数GetArrayLength?
2Q获取一个指向数l元素的指针Q?br>
代码4Q?br>
jint *body = (*env)->GetIntArrayElements(env, arr, 0);
使用GetIntArrayElementsҎ获取指向arr数组元素的指针,注意该函数的参数Q第一个是JNIEnvQ第二个是数l,W三个是数组里面W三个是数组里面开始的元素
3Q用指针取出Array中的元素
代码5Q?br>
for (i=0; i<len; i++) {
sum += body[i];
}
q里使用和普通的c中的数组使用没有什么不同了
4Q释放数l元素的引用
代码6Q?br>
(*env)->ReleaseIntArrayElements(env, arr, body, 0);
和操作String中的释放String的引用是一LQ提醒JVM回收arr数组元素的引用?br>
q里丄例子是用int数组的,同样q有boolean、float{对应的数组?br>
获取数组元素指针的对应关p:
函数 数组cd
GetBooleanArrayElements boolean
GetByteArrayElements byte
GetCharArrayElements char bitsCN.nET中国|管博客
GetShortArrayElements short
GetIntArrayElements int
GetLongArrayElements long
GetFloatArrayElements float
GetDoubleArrayElements double
释放数组元素指针的对应关p:
Function Array Type
ReleaseBooleanArrayElements boolean
ReleaseByteArrayElements byte
ReleaseCharArrayElements char
ReleaseShortArrayElements short
ReleaseIntArrayElements int
ReleaseLongArrayElements long
ReleaseFloatArrayElements float
ReleaseDoubleArrayElements double
讉K自定义Java对象数组
The JNI provides a separate set of functions to access elements of object arrays. You can use these functions to get and set individual object array elements.
Note: You cannot get all the object array elements at once.
GetObjectArrayElement returns the object element at a given index.
SetObjectArrayElement updates the object element at a given index.
3) 讉KJava对象的方法:
在本地方法中调用Java对象的方法的步骤Q?br>
?获取你需要访问的Java对象的类Q?br>
jclass cls = (*env)->GetObjectClass(env, obj);
使用GetObjectClassҎ获取obj对应的jclass?br>
?获取MethodIDQ?br>
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
使用GetMethdoIDҎ获取你要使用的方法的MethdoID。其参数的意义:
env??>JNIEnv
cls??>W一步获取的jclass
"callback"??>要调用的Ҏ?br>
"(I)V"??>Ҏ的Signature
?调用ҎQ?br>
(*env)->CallVoidMethod(env, obj, mid, depth);
使用CallVoidMethodҎ调用Ҏ。参数的意义Q?br>
env??>JNIEnv
obj??>通过本地ҎI过来的jobject
mid??>要调用的MethodIDQ即W二步获得的MethodIDQ?br>
depth??>Ҏ需要的参数Q对应方法的需求,d相应的参敎ͼ bitsCN.nET中国|管博客
注:q里使用的是CallVoidMethodҎ调用Q因为没有返回|如果有返回值的话用对应的ҎQ在后面会提到?br>
Ҏ的Signature
Ҏ的Signature是由Ҏ的参数和q回值的cd共同构成的,下面是他们的l构Q?br>
"(argument-types)return-type"
其中JavaE序中参数类型和其对应的值如下:
Signature Java中的cd
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class; fully-qualified-class
[ type type[]
( arg-types ) ret-type method type
一个JavacȝҎ的Signature可以通过javap命o获取Q?br>
javap -s -p Javacd
l调用的函数传参敎ͼ
通常我们直接在methodID后面要传的参数d在后面,但是q有其他的方法也可以传参敎ͼ
CallVoidMethodV可以获取一个数量可变的列表作ؓ参数Q?www_bitscn_com
CallVoidMethodA可以获取一个union?br>
调用静态方法:
是第二步和第三步调用的方法改为对应的Q?br>
GetStaticMethodID获取对应的静态方法的ID
CallStaticIntMethod调用静态方?br>
调用类的方法:
用的比较,自己看啦。^_^?br>
4)讉KJava对象的属性:
讉KJava对象的属性和讉KJava对象的方法基本上一P只需要将函数里面的Method改ؓField卛_
]]>
do
CLASSPATH="$CLASSPATH:""$jar"
done
windows :
SETLOCAL ENABLEDELAYEDEXPANSION
set LIB=xx
set CLASSPATH=.
FOR %%C IN (LIB\*.jar) DO set CLASSPATH=!CLASSPATH!;%%C
echo %CLASSPATH%
unix ksh:
暂无
]]>
1.4.2jdk~译执行。长旉执行没有发现有网上所说的׃jit优化D的当分配完Foo的内存,Foo构造函数未初始化完成就其地址赋值给foo的错误。相信这时候jit已经对代码进行了优化?br />
国外|址关于Double-Checked Locking的文?a >http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Paul Jakubik found an example of a use of double-checked locking that did not work correctly. A slightly cleaned up version of that code is available here.
When run on a system using the Symantec JIT, it doesn't work. In particular, the Symantec JIT compiles
singletons[i].reference = new Singleton();
to the following (note that the Symantec JIT using a handle-based object allocation system).
0206106A mov eax,0F97E78h
0206106F call 01F6B210 ; allocate space for
; Singleton, return result in eax
02061074 mov dword ptr [ebp],eax ; EBP is &singletons[i].reference
; store the unconstructed object here.
02061077 mov ecx,dword ptr [eax] ; dereference the handle to
; get the raw pointer
02061079 mov dword ptr [ecx],100h ; Next 4 lines are
0206107F mov dword ptr [ecx+4],200h ; Singleton's inlined constructor
02061086 mov dword ptr [ecx+8],400h
0206108D mov dword ptr [ecx+0Ch],0F84030h
As you can see, the assignment to singletons[i].reference is performed before the constructor for Singleton is called. This is completely legal under the existing Java memory model, and also legal in C and C++ (since neither of them have a memory model).
上面是国外网站给出的jit代码和说明?br />
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;
public class BCELTiming {
/**
* Add timing wrapper to method of class. The method can accept any
* arguments and return any type (including void), but must be a normal
* (non-static, non-initializer) method to be used with this code as
* currently implemented. Handling the other types of methods would not
* involve any fundamental changes to the code.
*
* @param cgen
* generator for class being modified
* @param method
* current method to be enhanced with timing wrapper
*/
private static void addWrapper(ClassGen cgen, Method method) {
// set up the construction tools
InstructionFactory ifact = new InstructionFactory(cgen);
InstructionList ilist = new InstructionList();
ConstantPoolGen pgen = cgen.getConstantPool();
String cname = cgen.getClassName();
MethodGen wrapgen = new MethodGen(method, cname, pgen);
wrapgen.setInstructionList(ilist);
// rename a copy of the original method
MethodGen methgen = new MethodGen(method, cname, pgen);
cgen.removeMethod(method);
String iname = methgen.getName() + "$impl";
methgen.setName(iname);
cgen.addMethod(methgen.getMethod());
//以上是一下初始化的工?br />
// compute the size of the calling parameters
// operand stack操作数堆?br /> Type[] types = methgen.getArgumentTypes(); // 取出参数cd数组
// solt代表本地变量的堆栈偏U量,里头储存了调用methen代表的函数的参数
int slot = methgen.isStatic() ? 0 : 1; // q种方式与Java如何处理Ҏ调用有关。对于非静态的ҎQ每ơ调用的W一个(隐藏的)参数是目标对象的this引用Q就是位|?储存的内容)?br /> for (int i = 0; i < types.length; i++) {
slot += types[i].getSize();// 累计个个参数cd的长度,
}
// 现在solt指向最后一个参数的下一个位|?/p>
// save time prior to invocation
// 调用静态的long java.lang.System.currentTimeMillis()Ҏ,调用l束后函数的q回的longcd的g压入operand stack操作数堆?br /> ilist.append(ifact.createInvoke("java.lang.System",
"currentTimeMillis", Type.LONG, Type.NO_ARGS,
Constants.INVOKESTATIC));
ilist.append(InstructionFactory.createStore(Type.LONG, slot));// operand stack的top保存到本地变量堆栈的slot位置,operand stack弹出long?/p>
// call the wrapped method
int offset = 0; // 偏移?br /> short invoke = Constants.INVOKESTATIC; // 预先讄用静态函?br /> if (!methgen.isStatic()) { // 如果不是调用静态函?调用的W一个(隐藏的)参数(目标对象的this引用)压入operand stack
ilist.append(InstructionFactory.createLoad(Type.OBJECT, 0));
offset = 1;// 偏移量加1
invoke = Constants.INVOKEVIRTUAL;// 讄用非静态函?br /> }
for (int i = 0; i < types.length; i++) { // 遍历所有参?br /> Type type = types[i];
ilist.append(InstructionFactory.createLoad(type, offset)); // 按参数类型把参数一个个从本地变量堆栈取出,压入operand stack
offset += type.getSize();
}
Type result = methgen.getReturnType();// 取得要调用函数的q回值类?br /> ilist.append(ifact.createInvoke(cname, iname, result, types, invoke));// 调用Ҏ名ؓiname的函?/p>
// store result for return later
if (result != Type.VOID) {
ilist.append(InstructionFactory.createStore(result, slot + 2)); // 名为iname的函数返回值复制到本地变量堆栈的slot+2的位|上
}
// print time required for method call
// 获取静态对象java.lang.System.out的引?q回值压入operand stack
ilist.append(ifact.createFieldAccess("java.lang.System", "out",
new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));
ilist.append(InstructionConstants.DUP);// 取operand stack的top,压入operand stack。完成后load_stack的头两个元素是静态对象java.lang.System.out的引?br /> ilist.append(InstructionConstants.DUP);// 取operand stack的top,压入operand stack。现在有3个java.lang.System.out的引用。供下面3ơ调用out.print()函数使用
String text = "Call to method " + methgen.getName() + " took ";
ilist.append(new PUSH(pgen, text));// text攑օpgenQ代表常量池Q?q把其在pgen的引用压入operand stack(供out.print(Sting)调用的参?
ilist.append(ifact.createInvoke("java.io.PrintStream", "print",
Type.VOID, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL));// 调用l束Qoperand stack弹出一个String的引用和一个out的引?q剩2个out),函数没有q回?/p>
ilist.append(ifact.createInvoke("java.lang.System",
"currentTimeMillis", Type.LONG, Type.NO_ARGS,
Constants.INVOKESTATIC));// 调用java.lang.System.currentTimeMillis()Ҏ,调用l束后函数的q回的longcd的g压入堆栈operand stack
ilist.append(InstructionFactory.createLoad(Type.LONG, slot));// 从本地变量堆栈的slot位置载入先前储存的long|压入operand stack
ilist.append(InstructionConstants.LSUB);// 调用long的减法指?弹出2个long|q把l果压入operand stack,现在operand stack的topW一个是longQ第二个是out的引?br /> ilist.append(ifact.createInvoke("java.io.PrintStream", "print",
Type.VOID, new Type[] { Type.LONG }, Constants.INVOKEVIRTUAL));// 调用out.print(long)Ҏ
ilist.append(new PUSH(pgen, " ms."));// String对象" ms."攑օpgen,q把其在pgen的引用压入operand stack(供out.print(Sting)调用的参?
ilist
.append(ifact.createInvoke("java.io.PrintStream", "println",
Type.VOID, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL));
// return result from wrapped method call
if (result != Type.VOID) {
ilist.append(InstructionFactory.createLoad(result, slot + 2));// 处理q回?如果不ؓI,从本地对象堆栈的slot+2位置d指定cd的返回值压入operand stack
}
ilist.append(InstructionFactory.createReturn(result)); //调用处理q回值的指oQresult回值的cd
//下面是一下扫ַ?br /> // finalize the constructed method
wrapgen.stripAttributes(true);
wrapgen.setMaxStack();
wrapgen.setMaxLocals();
cgen.addMethod(wrapgen.getMethod());
ilist.dispose();
}
public static void main(String[] argv) {
if (argv.length == 2 && argv[0].endsWith(".class")) {
try {
JavaClass jclas = new ClassParser(argv[0]).parse();
ClassGen cgen = new ClassGen(jclas);
Method[] methods = jclas.getMethods();
int index;
for (index = 0; index < methods.length; index++) {
if (methods[index].getName().equals(argv[1])) {
break;
}
}
if (index < methods.length) {
addWrapper(cgen, methods[index]);
FileOutputStream fos = new FileOutputStream(argv[0]);
cgen.getJavaClass().dump(fos);
fos.close();
} else {
System.err.println("Method " + argv[1] + " not found in "
+ argv[0]);
}
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
} else {
System.out.println("Usage: BCELTiming class-file method-name");
}
}
}
相对javassistQbcel实比较复杂。但是bcell我的感觉比较自由,有种一切尽在掌握的感觉Q这一Ҏ较我喜欢?br />虽然自由Q但是用bcel写一个类的实现绝对不会是一件让人开心的事情Q如果碰巧你的实现又E微复杂了点(比如实现了xx接口Q又增加了一些函数和成员变量)。而且你的实现未必会比javac~译出来的代码效率高Q不考虑jvm的动态优化)?br />不过Q先把你要实现的cȝ代码写出来,按照代码来写bcel的实玎ͼ会降低些实现隑ֺ?/p>