2015-12-26 3 views
2

У меня есть требование использовать пространство в доступной ОЗУ, на которое GC не контролирует. Я прочитал несколько статей на том же, что дало мне введение по двум подходам. Они указаны в следующем коде.Буфер против небезопасного - вне JVM

package com.directmemory; 

импорт java.lang.reflect.Field; import java.nio.ByteBuffer;

импорт sun.misc.Unsafe;

общественного класса DirectMemoryTest {

public static void main(String[] args) { 

    //Approach 1 
    ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(8); 
    directByteBuffer.putDouble(1.0); 
    directByteBuffer.flip(); 
    System.out.println(directByteBuffer.getDouble()); 

    //Approach 2 
    Unsafe unsafe = getUnsafe(); 
    long pointer = unsafe.allocateMemory(8); 
    unsafe.putDouble(pointer, 2.0); 
    unsafe.putDouble(pointer+8, 3.0); 

    System.out.println(unsafe.getDouble(pointer)); 
    System.out.println(unsafe.getDouble(pointer+8)); 
    System.out.println(unsafe.getDouble(pointer+16)); 
} 

public static Unsafe getUnsafe() { 
    try { 
     Field f = Unsafe.class.getDeclaredField("theUnsafe"); 
     f.setAccessible(true); 
     return (Unsafe) f.get(null); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return null; 
} 

}

У меня есть несколько вопросов

1) Почему я когда-либо обратить внимание на подход 1 упоминается в коде, потому что, согласно моему пониманию ByteBuffer.allocateDirect() не может вернуть мне буфер с объемом памяти более 2 ГБ? Поэтому, если мое требование состоит в том, чтобы хранить, скажем, 3 ГБ данных, я должен создать новый буфер и сохранить там данные, что означало бы, что помимо хранения данных у меня есть дополнительная ответственность за идентификацию соответствующего буфера (из списка 'n буферов), который поддерживает указатель на прямую память.

2) Не подходит подход 2 немного быстрее, чем подход 1, потому что мне не нужно сначала находить буфер, а затем данные, мне нужен механизм индексирования для поля объекта и использовать методы getDouble/getInt и передать абсолютный адрес?

3) Является ли выделение прямой памяти (правильно ли сказать кучную память?), Связанной с ПИД-кодом? Если на машине у меня есть два процесса java, вызовы allocateMemory в PID 1 и PID 2 дают мне никогда не пересекающиеся блоки памяти для использования?

4) Почему последний оператор sysout не приводит к 0,0? Идея состоит в том, что каждый двойной использует 8 байтов, поэтому я храню 1.0 по адресу, возвращенному allocateMemory, скажем, адрес = 1, 2.0 по адресу 1 + 8, который равен 9, а затем остановится. Значит, значение по умолчанию не должно быть 0.0?

ответ

4

Следует учитывать, что sun.misc.Unsafe не поддерживается API. Он будет заменен чем-то другим (http://openjdk.java.net/jeps/260)

1) Если ваш код должен работать без изменений с Java 8 до Java 10 (и более поздних версий), подход 1 с ByteBuffers - это путь.

Если вы готовы заменить использование sun.misc.Unsafe с любой заменой в Java 9/Java 10, вы вполне можете пойти с sun.misc.Unsafe.

2) Для очень больших структур данных с более чем 2 Гбайт подхода 2 может быть быстрее из-за необходимости дополнительного косвенного подхода в подходе 1. Однако без твердого (микро) теста я не был бы на нем ничего.

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

4) Вы назначаете 8 байтов неинициализированных памяти. Единственный объем памяти, который вы можете легально получить сейчас, составляет 8 байтов. Для памяти за пределами выделенного размера никаких гарантий не предусмотрено.

4a) Вы пишете 8 байт за пределами выделенной памяти (unsafe.putDouble(pointer+8, 3.0);), что уже приводит к повреждению памяти и может привести к сбою JVM при следующем распределении памяти.

4b) Вы читаете 16 байт за пределами выделенной памяти, которая (в зависимости от архитектуры процессора и операционной системы и использования предыдущей памяти) может привести к немедленному сбою JVM.

+0

Спасибо за все пункты. Первая точка, в которой я думаю, что ByteBuffer выигрывает над Unsafe, заключается в том, что ByteBuffer позволяет вам аккумулировать память контролируемым образом и имеет apis для доступа к позиции и ограничения, поэтому мы можем принять решение о создании нового буфера или нет, что я не уверен Небезопасным. Так что, если приложение требует выделения большего количества памяти с помощью небезопасных, не очень просто использовать метод reallocateMemory? Это имеет смысл или вы считаете, что перераспределение с использованием небезопасного является достаточно интуитивным? – 100pipers

+0

Если вы писали код в 'C', вы использовали бы аналогичные функции (malloc/realloc/free), поэтому использование reallocateMemory имеет смысл –