Подтвердить что ты не робот

Передача исходной памяти прямого буфера в Java для JOGL

Я использую прямые буферы (java.nio) для хранения информации о вершинах для JOGL. Эти буферы являются большими, и они заменяются несколько раз в течение срока службы. Память не освобождается во времени, и после нескольких замен я теряю память.

Кажется, что нет хорошего способа освободить использование классов буфера java.nio. Мой вопрос таков:

Есть ли какой-нибудь метод в JOGL для удаления прямых буферов? Я смотрю glDeleteBuffer(), но похоже, что это удаляет только буфер из памяти видеокарты.

Спасибо

4b9b3361

Ответ 1

Прямые буферы NIO используют неуправляемую память. Это означает, что они расположены в собственной куче, а не в куче Java. Как следствие, они освобождаются только тогда, когда JVM не хватает памяти в куче Java, а не в собственной куче. Другими словами, это неуправляемо = вам решать управлять ими. Принудительный сбор мусора не рекомендуется и большую часть времени не решает эту проблему.

Когда вы знаете, что прямой буфер NIO стал для вас бесполезным, вы должны освободить его собственную память, используя его sun.misc.Cleaner(StaxMan прав) и вызвать clean() (кроме Apache Harmony), вызвать free() (с Apache Harmony) или используйте лучший публичный API для этого (возможно, в Java> 12, AutoCleaning, расширяющий AutoCloseable?).

Это не работа JOGL, вы можете использовать простой Java-код, чтобы сделать это самостоятельно. Мой пример находится под лицензией GPL v2, а этот пример - под более разрешительной лицензией.

Edit.: Мой последний пример работает даже с Java 1.9 и поддерживает OpenJDK, Oracle Java, Sun Java, Apache Harmony, GNU Classpath и Android. Возможно, вам придется удалить синтаксический сахар, чтобы он работал с Java & lt; 1,7 (несколько уловов, алмазы и дженерики).

Ссылка: http://www.ibm.com/developerworks/library/j-nativememory-linux/

Direct ByteBuffer объекты автоматически очищают свои собственные буферы но может сделать это только как часть кучи Java GC - так они не делают автоматически реагировать на давление на родную кучу. GC происходит только когда куча Java становится настолько полной, она не может обслуживать выделение кучи запрос или если приложение Java явно запрашивает его (не рекомендуется, потому что это вызывает проблемы с производительностью).

Ссылка: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

Содержимое прямых буферов может находиться за пределами обычной кучи, собираемой мусором.

Это решение (в этом JEP, все еще черновик, вероятно, недоступно в Java 1.9) очень многообещающе, нам не нужно будет использовать непубличные API.

public long memory(long index) {
    // The scope where the memory region is available
    // Implements AutoClosable but 'close' can be called manually as well
    try (Scope scope = new NativeScope()) {
        // Allocate the actual memory area, in this case in the style of a "long-array"
        Pointer<Long> ptr = scope.allocate(
                    NativeLibrary.createLayout(long.class), numElements);

        // Get the reference to a certain element
        Reference<Long> ref = ptr.offset(index).deref();

        // Set a value to this element through the reference
        ref.set(Long.MAX_VALUE);

        // Read the value of an element
        return ref.get();
    }
}

Примечание: sun.misc.Cleaner был перемещен в jdk.internal.ref.Cleaner в Java 1.9 в модуле "java.base", последний реализовал Java. lang.Runnable (спасибо Алану Бейтману за напоминание об этой разнице) в течение короткого времени, но это больше не так. Вы должны вызвать sun.misc.Unsafe.invokeCleaner(), это сделано в JogAmp Gluegen. Я предпочел использовать Cleaner в качестве Runnable, поскольку он избегал полагаться на sun.misc.Unsafe, но теперь он не работает.

Мое последнее предложение работает с Java 9, 10, 11 и 12.

Существует хороший пример в Lucene под более разрешающей лицензией.

Ответ 2

Прямые буферы сложны и не имеют обычных гарантий сбора мусора - см. более подробную информацию: http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#direct

Если у вас возникли проблемы, я предлагаю выделить один раз и повторно использовать буфер, а не выделять и освобождать повторно.

Ответ 3

Способ, которым выполняется освобождение, ужасен - мягкая ссылка в основном вставляется в объект Cleaner, который затем выполняет освобождение при использовании ByteBuffer для сбора мусора. Но на самом деле это не гарантируется своевременно.

Ответ 4

Распределение прямого буфера - это работа, выполняемая сборщиком мусора через некоторое время после того, как объект ByteBuffer отмечен.

Вы можете попробовать сразу вызвать gc после удаления последней ссылки на ваш буфер. По крайней мере, есть вероятность, что память будет свободна немного быстрее.

Ответ 5

Вместо того, чтобы злоупотреблять отражением непубличного apis, вы можете сделать это тривиально, полностью в поддерживаемых публичных.

Напишите некоторый JNI, который обертывает malloc с помощью NewDirectByteBuffer (не забудьте установить порядок) и аналогичную функцию, чтобы освободить его.