2015-08-24 90 views
-1

我通過創建一個字符串對象(new String(「some string」);)進行了1億次的java和jni之間的時間差異測試。當通過Java代碼測試時,執行時間大約需要1秒。但是當通過Jni代碼測試時,大約需要31秒。Java字符串創建對象:Java和JNI中的執行時間差異

對於JNI部分,我是否需要更改jni代碼設計或需要添加其他編譯器選項以提高執行速度?

環境:
MinGW的(32)
視窗8.1(64)
爪哇1.8(32)
C++編譯器的選項:-shared -m32 -Wl, - 添加-STDCALL別名

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); 

在字符串對象的地方(爪哇/郎/串),我有也試過以下

  1. StringBuilder strObj =新的StringBuilder(int capacity = 100);
  2. Integer intObj = New Integer(int value = 100);

在所有情況下,它大約需要同一時間。

+2

你舉的例子清楚地表明,字節碼和本地代碼之間的轉換是一個相當昂貴的行動,所以我d建議僅將JNI用於不能在Java中本地執行的事情(如低級設備或操作系統交互),或者長時間的計算,其中發生的轉換損失超過執行例如,增加收益。 – fvu

+2

你不會在你的Java代碼和JNI代碼中做同樣的事情。無論如何,當你使用'NewStringUTF' JNI API「創建」一個字符串(加上'DeleteLocalRef')時會發生什麼? – manuell

+0

@manuell。謝謝。在fncall方法中處理DeleteLocalRef。 – Jeet

回答

1

但是在純Java中,優化編譯器可以「理解」字符串被創建爲立即銷燬,並且合法地什麼也不做。當涉及JNI時,Java和C都不能避免執行每一步。

+0

「優化編譯器可以理解字符串被創建立即銷燬」您的意思是字符串是不可變的意義上這是什麼意思? – Jeet

+1

不,我的意思是Java完全知道字符串範圍只有一行。 –

0

更改代碼如下執行時間從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); 
     } 
    } 
+0

在環路 –

+0

@AlexCohn中釋放類全局引用是錯誤的,全局引用釋放僅發生在循環外部。每次在Jni中創建時調用myInit()並在myCall()的末尾釋放。這裏全局引用(jclass)作爲多個Jni調用之間的緩存工作。或者你的意思是頻繁創建和刪除全球引用不應該發生(最小時間差)? – Jeet

+0

好的,每10^7一次並不重要 –