2012-03-09 14 views
12

मैं एक JNI कॉलबैक है:JNI संलग्न/अलग धागा स्मृति प्रबंधन

void callback(Data *data, char *callbackName){ 
    JNIEnv *env; 
    jvm->AttachCurrentThread((void **)&env, NULL); 
    /* start useful code*/ 

    /* end useful code */ 
    jvm->DetachCurrentThread(); 
} 

जब मैं इसे इस (खाली उपयोगी कोड) की तरह चलाने के लिए, मैं एक स्मृति रिसाव मिलता है। अगर मैं पूरी विधि पर टिप्पणी करता हूं, तो कोई रिसाव नहीं है। धागे को जोड़ने/अलग करने का सही तरीका क्या है?

मेरा एप्लिकेशन रीयल-टाइम ध्वनि डेटा को संसाधित करता है, इसलिए डेटा प्रोसेसिंग के लिए ज़िम्मेदार थ्रेड जितनी जल्दी हो सके एक और बैच के लिए तैयार होने के लिए किया जाना चाहिए। तो इन कॉलबैक के लिए, मैं नए धागे बनाते हैं। प्रत्येक सेकेंड में दर्जनों या यहां तक ​​कि उनमें से सैकड़ों हैं, वे खुद को JVM से जोड़ते हैं, एक कॉलबैक फ़ंक्शन को कॉल करते हैं जो एक ग्राफ को दोहराता है, अलग करता है और मर जाता है। क्या यह इस सामान को करने का सही तरीका है? लीकिंग मेमोरी को कैसे संभालें?

संपादित करें: टाइपो

ठीक मैं एक mimimal कोड की जरूरत पैदा की है:

package test; 

public class Start 
{ 
    public static void main(String[] args) throws InterruptedException{ 
     System.loadLibrary("Debug/JNITest"); 
     start(); 
    } 

    public static native void start(); 
} 

और

#include <jni.h> 
#include <Windows.h> 
#include "test_Start.h" 

JavaVM *jvm; 
DWORD WINAPI attach(__in LPVOID lpParameter); 

JNIEXPORT void JNICALL Java_test_Start_start(JNIEnv *env, jclass){ 
    env->GetJavaVM(&jvm); 
    while(true){ 
     CreateThread(NULL, 0, &(attach), NULL, 0, NULL); 
     Sleep(10); 
    } 
} 


DWORD WINAPI attach(__in LPVOID lpParameter){ 
    JNIEnv *env; 
    jvm->AttachCurrentThread((void **)&env, NULL); 
    jvm->DetachCurrentThread(); 
    return 0; 
} 

और जब मैं VisualJM प्रोफाइलर चलाने के लिए, मैं हमेशा की तरह sawtooth पैटर्न मिलता है, वहां कोई रिसाव नहीं है। हीप का उपयोग करीब 5 एमबी पर पहुंच गया। हालांकि, प्रक्रिया एक्सप्लोरर को देखते हुए वास्तव में कुछ अजीब व्यवहार दिखाते हैं: स्मृति धीरे-धीरे बढ़ रही है और बढ़ रही है, एक मिनट या 4 मिनट के लिए 4K एक सेकंड और फिर अचानक यह आवंटित स्मृति बूंदें। ये बूंदें कचरा संग्रह से मेल नहीं खाती हैं (वे अक्सर कम होते हैं और प्रोफाइलर में उन देखा-दांतों की तुलना में कम स्मृति को कम करते हैं)।

तो मेरी सबसे अच्छी शर्त यह है कि यह कुछ ओएस व्यवहार है जो हजारों मिलीसेकंद-जीवित धागे हैंडलिंग करता है। क्या कुछ गुरु के लिए इसका स्पष्टीकरण है? मूल कोड से जावा में वापस बुला के बारे में

उत्तर

7

मुझे समस्या का पता चला। यह जेएनआई कोड में स्थानीय संदर्भों को लटक रहा था जिसे मैंने नष्ट नहीं किया था। प्रत्येक कॉलबैक एक नया स्थानीय संदर्भ बनाएगा, जिसके परिणामस्वरूप मेमोरी लीक हो जाएगी। जब मैंने स्थानीय संदर्भ को वैश्विक रूप में परिवर्तित किया, तो मैं इसका पुन: उपयोग कर सकता था, समस्या गायब हो गई।

14

कई अंक:

  • AttachCurrentThread केवल तभी jvm- बुलाया जाना चाहिए> GetEnv() एक शून्य मान देता है। अगर थ्रेड पहले से जुड़ा हुआ है तो यह आमतौर पर नो-ऑप होता है, लेकिन आप कुछ ओवरहेड को बचा सकते हैं।
  • DetachCurrent थ्रेड केवल तभी बुलाया जाना चाहिए यदि आपने AttachCurrentThread को बुलाया था।
  • अगर आप भविष्य में उसी धागे पर कॉल होने की उम्मीद करते हैं तो अलग से बचें।

अपने मूल कोड के सूत्रण व्यवहार के आधार पर आप अलग से बचने और बजाय (समाप्ति पर निपटान के लिए सभी देशी धागे के लिए संदर्भ की दुकान करता है, तो आप भी है कि क्या करने की जरूरत कर सकते हैं, आप आवेदन पर भरोसा करने में सक्षम हो सकता साफ करने के लिए बंद करें)।

यदि आप लगातार मूल धागे को संलग्न और अलग करते हैं, तो वीएम को लगातार जावा ऑब्जेक्ट्स के साथ धागे (अक्सर वही) जोड़ना चाहिए। कुछ वीएम प्रदर्शन को बेहतर बनाने के लिए धागे का उपयोग कर सकते हैं, या अस्थायी रूप से कैश मैपिंग का उपयोग कर सकते हैं, लेकिन यदि आप वीएम पर निर्भर नहीं हैं तो आप बेहतर और अधिक अनुमानित व्यवहार प्राप्त करेंगे।

+0

मैं आपका बिंदु देखता हूं और मैं आम तौर पर सहमत हूं। जो धागे मैं बना रहा हूं वे वास्तव में अल्पकालिक हैं। मैं उन्हें (WinApi CreateThread का उपयोग करके) बना देता हूं, फिर तुरंत उन्हें JVM से संलग्न करता हूं।जावा में, वे एक नए मूल्य के साथ एक स्विंग ग्राफ repaint। जब वे पूरा हो जाते हैं, तो वे निष्पादन को रोकते हैं और बंद कर देते हैं (वापसी 0), जिस बिंदु पर उन्हें ओएस द्वारा नष्ट किया जाना चाहिए। वे जावा के लिए एक नया मूल्य पारित करने के लिए उपयोग किया जाता है। प्रत्येक ध्वनि चैनल के लिए उनमें से 40 प्रत्येक सेकेंड हैं (मैं 32 चैनल तक काम करता हूं)। –

+0

आप थ्रेड पूलिंग पर विचार कर सकते हैं। अपने धागे को लंबे समय तक बने रहें और लगातार नए धागे को बढ़ाने के बजाए कतार से इनपुट लें। इससे ओएस और जावा दोनों में थ्रेड प्रबंधन ओवरहेड कम हो जाएगा। – technomage

+0

यदि आप किसी विधि को कॉल करने के अलावा जेएनआई में कोई जावा सामान कर रहे हैं, तो आप उन कार्रवाइयों के आस-पास एक स्थानीय फ्रेम को धक्का/पॉप भी कर सकते हैं। – technomage

संबंधित मुद्दे