JNA/ByteBuffer لا تحصل على تحرير و تسبب ج كومة من نفاد الذاكرة

StackOverflow https://stackoverflow.com/questions/1744533

  •  20-09-2019
  •  | 
  •  

سؤال

اسمحوا لي أن ابدأ بالقول أن فهمي كيف JNA جافا الأصلي المباشر تخصيص الذاكرة هو الحشوية في أحسن الأحوال ، لذا أنا أحاول أن تصف لي فهم ما يجري.أي تصحيحات بالإضافة إلى الردود ستكون كبيرة...

أنا تشغيل تطبيق الذي يمزج Java و C التعليمات البرمجية الأصلية باستخدام الجيش الشعبي اليوغوسلافي وأنا تشغيل عبر إنتاج سلعة المسألة مع جافا جامع القمامة عدم مجانا المراجع الأصلي المباشر تخصيص الذاكرة ، مما أدى إلى ج كومة نفاد الذاكرة.

أنا واثق من أن بلدي C التطبيق ليس مصدر تخصيص المسألة ، أنا تمرير java.nio.ByteBuffer في التعليمات البرمجية C, تعديل العازلة ، ثم الوصول إلى نتيجة في جافا وظيفة.لدي واحدة malloc واحد المقابلة free خلال كل وظيفة الدعوة ، ولكن بعد مرارا وتكرارا تشغيل التعليمات البرمجية في جافا malloc سوف تفشل في نهاية المطاف.

هنا هو إلى حد ما الاستهانة بها مجموعة من التعليمات البرمجية التي يسلك هذه المسألة - واقعيا أحاول تخصيص حوالي 16-32MB على C كومة من خلال وظيفة الاتصال.

بلدي كود جافا لا شيء من هذا القبيل:

public class MyClass{
    public void myfunction(){
        ByteBuffer foo = ByteBuffer.allocateDirect(1000000);
        MyDirectAccessLib.someOp(foo, 1000000);
        System.out.println(foo.get(0));
    }
}

public MyDirectAccessLib{
    static {
        Native.register("libsomelibrary");
    }
    public static native void someOp(ByteBuffer buf, int size);
}

ثم رمز C قد يكون شيئا مثل:

#include <stdio.h>
#include <stdlib.h>
void someOp(unsigned char* buf, int size){
    unsigned char *foo;
    foo = malloc(1000000);
    if(!foo){
        fprintf(stderr, "Failed to malloc 1000000 bytes of memory\n");
        return;
    }
    free(foo);

    buf[0] = 100;
}

المشكلة هي بعد استدعاء هذه الدالة مرارا وتكرارا Java هو مستقر إلى حد ما (وهي تنمو ببطء) ، ولكن ج وظيفة في نهاية المطاف لا يمكن تخصيص المزيد من الذاكرة.على مستوى عال وأعتقد أن هذا هو لأن جافا تخصيص الذاكرة إلى C كومة ، ولكن ليس تنظيف ByteBuffer أن نقاط في هذه الذاكرة لأن جافا ByteBuffer كائن صغير نسبيا.

حتى الآن لقد وجدت تشغيل GC يدويا في وظيفة ما يلزم تنظيف, ولكن هذا يبدو وكأنه كل فكرة الفقيرة والفقراء الحل.

كيف يمكن إدارة هذه المشكلة بشكل أفضل بحيث ByteBuffer الفضاء المناسب تحررت و ج كومة الفضاء يتم التحكم?

هو فهم المشكلة غير صحيحة (هل هناك شيء أنا تشغيل بشكل غير صحيح)?

تحرير:تعديل أحجام المخازن المؤقتة إلى أن تكون أكثر تعبيرا من التطبيق الفعلي ، أنا تخصيص الصور تقريبا 3000x2000...

هل كانت مفيدة؟

المحلول

وأعتقد أن كنت قد شخصت بشكل صحيح: أنك لن ينفد من كومة جاوة، وبالتالي فإن JVM لا جمع القمامة، ولم يتم تحرير المخازن المؤقتة المعينة. حقيقة أن لم يكن لديك مشاكل عند تشغيل GC يبدو يدويا لتأكيد هذا. يمكنك أيضا تشغيل مطول تسجيل جمع تأكيدا الثانوي.

وذلك ما يمكن أن تفعله؟ حسنا، أول شيء سوف أحاول هو الحفاظ على الأولي JVM كومة حجم صغير، وذلك باستخدام وسيطة سطر الأوامر -Xms. هذا يمكن أن يسبب مشاكل، إذا كان البرنامج هو تخصيص باستمرار الذاكرة كميات صغيرة على كومة جاوة، كما أنه سيتم تشغيل GC أكثر تواترا.

وكنت أيضا استخدام <م> PMAP أداة (أو ما يعادلها على ويندوز) لدراسة خريطة الذاكرة الظاهرية. فمن الممكن أن كنت تفتيت كومة C، من خلال تخصيص مخازن-متغير الحجم. إذا كان هذا هو الحال، ثم سترى كل خريطة افتراضية أكبر، مع وجود ثغرات بين الكتل "حالا". والحل هناك هو تخصيص كتل بحجم ثابت التي هي أكبر من التي تحتاج إليها.

نصائح أخرى

كنت في الواقع تواجه علة معروفة في Java VM.أفضل حل المدرجة في علة التقرير هو:

  • "إن -XX:MaxDirectMemorySize= الخيار يمكن استخدامها للحد من كمية المباشر الذاكرة المستخدمة.محاولة تخصيص المباشر للذاكرة من شأنه أن يسبب هذا الحد إلى تجاوز أسباب كامل GC وذلك لإثارة إشارة تجهيز وإطلاق unreferenced المخازن المؤقتة."

غيرها من الحلول الممكنة ما يلي:

  • إدراج بعض الأحيان صريحة النظام.gc() الدعاء لضمان المباشر مخازن هي المستصلحة.
  • تقليل حجم جيل الشباب إلى قوة أكثر تواترا GCs.
  • صراحة حمام السباحة مباشرة مخازن على مستوى التطبيق.

إذا كنت تريد حقا أن تعتمد على المباشر بايت مخازن, ثم أود أن أقترح تجميع على مستوى التطبيق.اعتمادا على تعقيد التطبيق الخاص بك حتى أنك قد ببساطة ذاكرة التخزين المؤقت وإعادة استخدام نفس العازلة (حذار من خيوط متعددة).

وأظن مشكلتك ويرجع ذلك إلى استخدام <م> مباشرة مخازن بايت. ويمكن تخصيص خارج كومة جافا.

إذا كنت استدعاء الأسلوب في كثير من الأحيان، وتخصيص مخازن صغيرة في كل مرة، ونمط الاستخدام الخاص بك قد لا يكون مناسبا لمنطقة عازلة المباشر.

في أجل عزل المشكلة، فما استقاموا لكم فاستقيموا التحول إلى (جافا) العازلة المخصصة كومة (مجرد استخدام طريقة allocate في مكان allocateDirect. وإذا كان هذا يجعل مشكلة الذاكرة الخاصة بك الذهاب بعيدا، وكنت قد وجدت الجاني. سوف يكون السؤال التالي ما إذا كان <م> مباشرة عازلة بايت لديه أي ميزة الأداء والحكمة، وإذا لم (وأنا أخمن أنه لا)، فإنك لن تحتاج إلى القلق حول كيفية تنظيف الامر بشكل صحيح.

إذا كنت قد نفد من الذاكرة كومة، وأثار GC تلقائيا. ولكن إذا كنت قد نفد من الذاكرة مباشرة، لا يتم تشغيل GC (على JVM الشمس على الأقل)، وكنت مجرد الحصول على OutOfMemoryError حتى لو كان GC شأنه ان يحرر ذاكرة كافية. لقد وجدت لديك لتحريك GC يدويا في هذه الحالة.

وربما يكون الحل الأفضل لإعادة استخدام نفس ByteBuffer لذلك لا تحتاج إلى إعادة acllocate ByteBuffers.

إلى الحرة المباشرة Buffer's [1] الذاكرة يمكنك استخدام JNI.

وظيفة GetDirectBufferAddress(JNIEnv* env, jobject buf)[3] من JNI 6 API يمكن استخدامها للحصول على مؤشر إلى الذاكرة Buffer ثم القياسية free(void *ptr) الأمر مؤشر إلى تحرير الذاكرة.

بدلا من كتابة التعليمات البرمجية مثل ج للاتصال قال وظيفة من جاوة ، يمكنك استخدام JNA's Native.getDirectBufferPointer(Buffer)[6]

الشيء الوحيد الذي غادر بعد ذلك إلى التخلي عن كل ما يشير إلى Buffer الكائن.جافا جمع القمامة ثم تحرير Buffer على سبيل المثال كما هو الحال مع أي دولة أخرى غير المشار إليها الكائن.

يرجى ملاحظة أن المباشر Buffer ليس بالضرورة خريطة 1:1 إلى الذاكرة المخصصة المنطقة.على سبيل المثال JNI API NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)[7].على هذا النحو, يجب عليك فقط مجانا الذاكرة Bufferالصورة التي في تخصيص الذاكرة المنطقة تعرف أن واحد إلى واحد مع الذاكرة الأصلية.

أنا أيضا لا أعرف إذا كان يمكنك تحرير مباشرة Buffer تم إنشاؤها بواسطة جافا ByteBuffer.allocateDirect(int)[8] بالضبط نفس السبب أعلاه.يمكن أن يكون JVM أو منصة جافا تنفيذ تفاصيل محددة سواء كانوا من استخدام المسبح أو لا 1:1 تخصيص الذاكرة عندما يوزعون جديدة مباشرة Buffers.

هنا يلي معدلة قليلا مقتطف من مكتبتي فيما يتعلق المباشر ByteBuffer[9] التعامل مع (يستخدم JNA Native[10] و Pointer[11] دروس):

/**
 * Allocate native memory and associate direct {@link ByteBuffer} with it.
 * 
 * @param bytes - How many bytes of memory to allocate for the buffer
 * @return The created {@link ByteBuffer}.
 */
public static ByteBuffer allocateByteBuffer(int bytes) {
        long lPtr = Native.malloc(bytes);
        if (lPtr == 0) throw new Error(
            "Failed to allocate direct byte buffer memory");
        return Native.getDirectByteBuffer(lPtr, bytes);
}

/**
 * Free native memory inside {@link Buffer}.
 * <p>
 * Use only buffers whose memory region you know to match one to one
 * with that of the underlying allocated memory region.
 * 
 * @param buffer - Buffer whose native memory is to be freed.
 * The class instance will remain. Don't use it anymore.
 */
public static void freeNativeBufferMemory(Buffer buffer) {
        buffer.clear();
        Pointer javaPointer = Native.getDirectBufferPointer(buffer);
        long lPtr = Pointer.nativeValue(javaPointer);
        Native.free(lPtr);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top