Java 字符串对象创建:Java 和 JNI 中的执行时间不同
Java String Object creation : Execution time difference in both Java & in JNI
我做了一个java和jni的时间差测试,创建一个字符串对象(new String("some string");)一亿次。当通过 Java 代码测试时,执行大约需要 1 秒。但是当通过 Jni 代码测试它时,它花费了大约 31 秒。
对于JNI部分,我是否需要更改jni代码设计或需要添加额外的编译器选项来提高执行速度?
环境:
明威 (32)
Windows 8.1 (64)
Java1.8 (32)
C++ 编译器选项:-shared -m32 -Wl,--add-stdcall-alias
(下面给出的每个文件中需要部分代码)
C++ 文件:
class javaString {
jclass cls;
jmethodID method;
jstring strNew;
jobject obj;
public:
javaString() {
}
void myInit(JNIEnv *env) {
jclass cls1 = env-> FindClass("java/lang/String");
cls = (jclass) env->NewGlobalRef(cls1);
env->DeleteLocalRef(cls1);
method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
}
void myCall(JNIEnv *env, long lng) {
strNew = env->NewStringUTF("Apple");
for (long i = 0; i < lng; i++) {
obj = env->NewObject(cls, method, strNew);
env->DeleteLocalRef(obj);
}
env->DeleteLocalRef(strNew);
env->DeleteGlobalRef(cls);
}
};
javaString objStr;
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnInit
(JNIEnv *env, jobject obj) {
objStr.myInit(env);
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnCall
(JNIEnv *env, jobject obj, jint a) {
long lng = a;
objStr.myCall(env, lng);
}
Java 文件:
public class clsNative {
public native void fnInit();
public native void fnCall(int a);
public void fnProcess2(int a){
for(int i=0;i<a;i++){
String str = new String("Apple");
str=null;
}
}
}
Java 文件(测试):
clsNative a = new clsNative();
boolean blnJNITest=true;
String s1, s2;
s1 = Calendar.getInstance().getTime().toLocaleString();
int ii = 100000000; //100 million
if (blnJNITest) {
a.fnInit();
a.fnCall(ii);
} else {
a.fnProcess2(ii);
}
s2 = Calendar.getInstance().getTime().toLocaleString();
System.out.println(s1);
System.out.println(s2);
代替字符串对象(java/lang/String),我也尝试了下面的方法
- StringBuilder strObj = New StringBuilder(int capacity = 100);
- 整数 intObj = 新整数(int 值 = 100);
在所有情况下,它花费的时间都差不多。
但在纯 Java 中,优化编译器可以 "understand" 创建的字符串立即被销毁,合法地不做任何事情。当涉及到JNI时,Java和C都不可能避免执行每一步。
如下更改代码,执行时间从 31 秒更改为大约 9 秒。
C++ 文件:
void myInit(JNIEnv *env) {
jclass cls1 = env-> FindClass("java/lang/String");
cls = (jclass) env->NewGlobalRef(cls1);
env->DeleteLocalRef(cls1);
method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
}
void myCall(JNIEnv *env, long lng) {
strNew = env->NewStringUTF("Apple");
jobjectArray objarr = env->NewObjectArray(lng, cls,strNew);
if (objarr != NULL) {
cout << "Array constructed" << endl;
for (long i = 0; i < lng; i++) {
obj = env->GetObjectArrayElement(objarr, i);
env->DeleteLocalRef(obj);
}
} else {
cout << "Array construction failed" << endl;
}
env->DeleteLocalRef(strNew);
env->DeleteLocalRef(objarr); /*Not sure is this 2nd line need*/
env->DeleteGlobalRef(cls);
}
Java 文件(测试):
int ii = 10000000; //10 million
if (blnJNITest) {
for(int i=0;i<10;i++){ //call 10 times
a.fnInit();
a.fnCall(ii);
}
}
我做了一个java和jni的时间差测试,创建一个字符串对象(new String("some string");)一亿次。当通过 Java 代码测试时,执行大约需要 1 秒。但是当通过 Jni 代码测试它时,它花费了大约 31 秒。
对于JNI部分,我是否需要更改jni代码设计或需要添加额外的编译器选项来提高执行速度?
环境:
明威 (32)
Windows 8.1 (64)
Java1.8 (32)
C++ 编译器选项:-shared -m32 -Wl,--add-stdcall-alias
(下面给出的每个文件中需要部分代码)
C++ 文件:
class javaString {
jclass cls;
jmethodID method;
jstring strNew;
jobject obj;
public:
javaString() {
}
void myInit(JNIEnv *env) {
jclass cls1 = env-> FindClass("java/lang/String");
cls = (jclass) env->NewGlobalRef(cls1);
env->DeleteLocalRef(cls1);
method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
}
void myCall(JNIEnv *env, long lng) {
strNew = env->NewStringUTF("Apple");
for (long i = 0; i < lng; i++) {
obj = env->NewObject(cls, method, strNew);
env->DeleteLocalRef(obj);
}
env->DeleteLocalRef(strNew);
env->DeleteGlobalRef(cls);
}
};
javaString objStr;
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnInit
(JNIEnv *env, jobject obj) {
objStr.myInit(env);
}
JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnCall
(JNIEnv *env, jobject obj, jint a) {
long lng = a;
objStr.myCall(env, lng);
}
Java 文件:
public class clsNative {
public native void fnInit();
public native void fnCall(int a);
public void fnProcess2(int a){
for(int i=0;i<a;i++){
String str = new String("Apple");
str=null;
}
}
}
Java 文件(测试):
clsNative a = new clsNative();
boolean blnJNITest=true;
String s1, s2;
s1 = Calendar.getInstance().getTime().toLocaleString();
int ii = 100000000; //100 million
if (blnJNITest) {
a.fnInit();
a.fnCall(ii);
} else {
a.fnProcess2(ii);
}
s2 = Calendar.getInstance().getTime().toLocaleString();
System.out.println(s1);
System.out.println(s2);
代替字符串对象(java/lang/String),我也尝试了下面的方法
- StringBuilder strObj = New StringBuilder(int capacity = 100);
- 整数 intObj = 新整数(int 值 = 100);
在所有情况下,它花费的时间都差不多。
但在纯 Java 中,优化编译器可以 "understand" 创建的字符串立即被销毁,合法地不做任何事情。当涉及到JNI时,Java和C都不可能避免执行每一步。
如下更改代码,执行时间从 31 秒更改为大约 9 秒。
C++ 文件:
void myInit(JNIEnv *env) {
jclass cls1 = env-> FindClass("java/lang/String");
cls = (jclass) env->NewGlobalRef(cls1);
env->DeleteLocalRef(cls1);
method = env->GetMethodID(cls, "<init>", "(Ljava/lang/String;)V");
}
void myCall(JNIEnv *env, long lng) {
strNew = env->NewStringUTF("Apple");
jobjectArray objarr = env->NewObjectArray(lng, cls,strNew);
if (objarr != NULL) {
cout << "Array constructed" << endl;
for (long i = 0; i < lng; i++) {
obj = env->GetObjectArrayElement(objarr, i);
env->DeleteLocalRef(obj);
}
} else {
cout << "Array construction failed" << endl;
}
env->DeleteLocalRef(strNew);
env->DeleteLocalRef(objarr); /*Not sure is this 2nd line need*/
env->DeleteGlobalRef(cls);
}
Java 文件(测试):
int ii = 10000000; //10 million
if (blnJNITest) {
for(int i=0;i<10;i++){ //call 10 times
a.fnInit();
a.fnCall(ii);
}
}