Буфер против небезопасных - вне JVM

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

package com.directmemory;

import java.lang.reflect.Field; импорт 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) Связано ли выделение прямой памяти (правильно ли выделять кучу памяти?) С PID? Если на машине у меня есть 2 процесса Java, вызовы allocateMemory в PID 1 и PID 2 дают мне никогда не пересекающиеся блоки памяти для использования?

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

1 ответ

Решение

Следует учитывать, что 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 байт. Для памяти сверх вашего выделенного размера никаких гарантий не дается.

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

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

Другие вопросы по тегам