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

Примеры форсирования освобождения собственной памяти напрямую ByteBuffer выделили, используя sun.misc.Unsafe?

JDK обеспечивает возможность размещения так называемых прямых байтовых буферов, где память выделяется вне кучи Java. Это может быть полезно, поскольку эта память не затрагивается сборщиком мусора и, как таковая, не вносит вклад в накладные расходы GC: это очень полезно для свойств долгоживущих вещей, таких как кеши.

Однако существует одна критическая проблема с существующей реализацией: базовая память выделяется асинхронно только тогда, когда владелец ByteBuffer собирает мусор; нет способа принудительного раннего освобождения. Это может быть проблематично, так как сам цикл GC не зависит от обработки ByteBuffers, и учитывая, что ByteBuffers, вероятно, будут находиться в области памяти Old Generation, возможно, что GC называется часами после того, как ByteBuffer больше не используется.

Но теоретически должно быть возможно использовать методы sun.misc.Unsafe (freeMemory, allocateMemory) напрямую: это то, что сам JDK использует для выделения/деаллокации собственной памяти. Глядя на код, я вижу потенциальную проблему с возможностью двойного освобождения памяти, поэтому я бы хотел убедиться, что состояние будет правильно очищено.

Может ли кто-нибудь указать мне на код, который это делает? В идеале хотелось бы использовать это вместо JNA.

ПРИМЕЧАНИЕ. Я видел этот вопрос, который является родственным.

Похоже, что ответы, на которые указывают ответы, являются хорошим способом: здесь является примером кода из Elastic Search, который использует эту идею. Спасибо всем!

4b9b3361

Ответ 1

Использование sun.misc.Unsafe вряд ли возможно, потому что базовый адрес выделенной основной памяти является локальной переменной конструктора java.nio.DirectByteBuffer.

На самом деле вы можете принудительно освободить собственную память следующим кодом:

import sun.misc.Cleaner;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;

...

public static void main(String[] args) throws Exception {
    ByteBuffer direct = ByteBuffer.allocateDirect(1024);
    Field cleanerField = direct.getClass().getDeclaredField("cleaner");
    cleanerField.setAccessible(true);
    Cleaner cleaner = (Cleaner) cleanerField.get(direct);
    cleaner.clean();
}

Ответ 2

Существует гораздо более простой способ очистки памяти.

public static void clean(ByteBuffer bb) {
    if(bb == null) return;
    Cleaner cleaner = ((DirectBuffer) bb).cleaner();
    if (cleaner != null) cleaner.clean();
}

Использование этого может иметь большое значение, если вы отбрасываете прямой или сопоставленный с памятью ByteBuffer довольно быстро.

Одной из причин использования Cleaner для этого является то, что вы можете иметь несколько копий базовых ресурсов памяти, например. с срезом(). и у Cleaner есть количество ресурсов из них.

Ответ 3

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