Выделение собственной памяти прямого буфера в Java для JOGL - PullRequest
12 голосов
/ 16 августа 2010

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

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

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

Спасибо

Ответы [ 5 ]

15 голосов
/ 06 ноября 2014

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

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

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

Редактировать .: Мой последний пример работает даже с Java 1.9 и поддерживает OpenJDK, Oracle Java, Sun Java, Apache Harmony, GNU Classpath и Android. Возможно, вам придется удалить некоторый синтаксический сахар, чтобы он работал с Java <1.7 (мульти-уловы, алмазы и дженерики). </p>

Ссылка: 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();
    }
}

NB: sun.misc.Cleaner был перемещен в jdk.internal.ref.Cleaner в Java 1.9 в модуле "java.base", но последний реализует Java. lang.Runnable (спасибо Алану Бейтману за напоминание об этой разнице). Затем вам просто нужно привести очиститель к Runnable и вызвать метод run() (не вызывайте его по отражению, чтобы избежать получения исключения java.lang.IllegalAccessException). Это работает, я только что протестировал (6 августа 2016 г.) с Java 1.9 Early Access build 129.

Однако jdk.internal.ref.Cleaner может быть перемещен в java.ref.Cleaner $. Очистится позже.

В Lucene есть хороший пример

с более разрешительной лицензией. *1048*
3 голосов
/ 17 августа 2010

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

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

1 голос
/ 11 декабря 2011

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

0 голосов
/ 19 мая 2016

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

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

0 голосов
/ 17 августа 2010

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

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

...