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

Общая память между двумя JVM

Есть ли способ в JAVA для двух JVM (работающих на одной физической машине) для использования/совместного использования одного и того же адресного пространства mermory? Предположим, что производитель в JVM1 помещает сообщения в определенную предварительно определенную ячейку памяти, может ли потребитель на JVM2 извлекать сообщение, если он знает, к какой ячейке памяти следует смотреть?

4b9b3361

Ответ 1

Решение 1:

Лучшим решением, на мой взгляд, является использование файлов с отображением памяти. Это позволяет вам обмениваться областью памяти между любым количеством процессов, включая другие не-Java-программы. Вы не можете помещать объекты Java в файл с отображением памяти, если вы не сериализуете их. Следующий пример показывает, что вы можете общаться между двумя разными процессами, но вам нужно сделать его намного более сложным, чтобы обеспечить лучшую связь между процессами. Я предлагаю вам посмотреть пакет Java NIO, в частности классы и методы, используемые в приведенных ниже примерах.

Сервер:

public class Server {

    public static void main( String[] args ) throws Throwable {
        File f = new File( FILE_NAME );

        FileChannel channel = FileChannel.open( f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );

        MappedByteBuffer b = channel.map( MapMode.READ_WRITE, 0, 4096 );
        CharBuffer charBuf = b.asCharBuffer();

        char[] string = "Hello client\0".toCharArray();
        charBuf.put( string );

        System.out.println( "Waiting for client." );
        while( charBuf.get( 0 ) != '\0' );
        System.out.println( "Finished waiting." );
    }
}

Клиент:

public class Client {

    public static void main( String[] args ) throws Throwable {
        File f = new File( FILE_NAME );
        FileChannel channel = FileChannel.open( f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE );

        MappedByteBuffer b = channel.map( MapMode.READ_WRITE, 0, 4096 );
        CharBuffer charBuf = b.asCharBuffer();

        // Prints 'Hello server'
        char c;
        while( ( c = charBuf.get() ) != 0 ) {
            System.out.print( c );
        }
        System.out.println();

        charBuf.put( 0, '\0' );
    }

}

Решение 2:

Другим решением является использование Java Sockets для обмена данными между процессами. Это имеет дополнительное преимущество, позволяющее легко общаться через сеть. Можно утверждать, что это медленнее, чем использование файлов с отображением памяти, но у меня нет никаких тестов, чтобы поддержать этот вывод. Я не буду отправлять код для реализации этого решения, так как это может стать очень сложным для реализации надежного сетевого протокола и является довольно специфичным для приложения. Существует много хороших сетевых сайтов, которые можно найти с помощью быстрого поиска.


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

Это, как говорится, довольно интересно экспериментировать. Для чтения/записи в произвольную память в текущем процессе вы можете использовать класс sun.misc.Unsafe. Это предоставляется на всех JVM, которые я знаю и использовал. Пример использования класса можно найти здесь.

Ответ 2

Есть несколько библиотек IPC, которые облегчают использование общей памяти через файлы с отображением памяти в Java.

Chronicle-Queue

Chronicle Queue похожа на неблокирующую Java Queue, за исключением того, что вы можете предложить сообщение в одной JVM и опросить его в другой JVM.

В обоих JVM вы должны создать экземпляр ChronicleQueue в том же каталоге FS (найдите этот каталог в FS, установленном на память, если вам не требуется постоянство сообщений):

ChronicleQueue ipc = ChronicleQueueBuilder.single("/dev/shm/queue-ipc").build();

Напишите сообщение в одном JVM:

ExcerptAppender appender = ipc.acquireAppender();
appender.writeDocument(w -> {
    w.getValueOut().object(message);
});

Прочитайте сообщение в другой JVM:

ExcerptTailer tailer = ipc.createTailer();
// If there is no message, the lambda, passed to the readDocument()
// method is not called.
tailer.readDocument(w -> {
    Message message = w.getValueIn().object(Message.class);
    // process the message here
});

// or avoid using lambdas
try (DocumentContext dc = tailer.readingDocument()) {
    if (dc.isPresent()) {
        Message message = dc.wire().getValueIn().object(Message.class);
        // process the message here
    } else {
        // no message
    }
}

Aeron IPC

Aeron - это больше, чем просто очередь IPC (это инфраструктура сетевой коммуникации), но она также обеспечивает функциональность IPC. Это похоже на хронологическую очередность, одно важное отличие состоит в том, что она использует библиотеку SBE для сортировки сообщений/демаршаллинга, а в хроническом порядке используется Chronicle Wire.

Хроника карты

Chronicle Map позволяет передавать IPC некоторым ключом. В обеих JVM вы должны создать карту с идентичными конфигурациями и сохранить ее в том же файле (файл должен быть локализован в FS, установленном на жестком диске, если вам не требуется постоянная жесткость диска, например, в /dev/shm/):

Map<Key, Message> ipc = ChronicleMap
    .of(Key.class, Message.class)
    .averageKey(...).averageValue(...).entries(...)
    .createPersistedTo(new File("/dev/shm/jvm-ipc.dat"));

Затем в одной JVM вы можете написать:

ipc.put(key, message); // publish a message

В приемнике JVM:

Message message = ipc.remove(key);
if (message != null) {
    // process the message here
}

Ответ 3

Честно говоря, вы не хотите делиться одной и той же памятью. Вы должны отправить только те данные, которые вам нужны для другой JVM. При этом, если вам нужна общая память, существуют другие решения.

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

Однако вы можете отправить данные в другую JVM и обратно различными способами:

1) Используя RMI, вы можете настроить удаленный сервер для анализа данных. Я нашел для себя немного хлопот, потому что он требует изменений безопасности и что данные Serializable. Вы можете узнать больше по ссылке.

2) Использование сервера - это вековой метод отправки данных в разные места. Один из способов реализации этого - использование ServerSocket и соединение с Socket в localhost. Объекты по-прежнему должны быть Serializable, если вы хотите использовать ObjectOutputStream.


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

Если вы хотите использовать Java-код, вы можете взглянуть на использование s.m.Unsafe, используя правильные адреса памяти, вы сможете восстановить сохраненные объекты с помощью базовых массивов C/С++ в ОС.

В противном случае вы можете использовать методы native для непосредственного доступа к массивам C/С++, хотя я не знаю, как это можно реализовать.

Ответ 4

Да,

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

Например, вы можете написать кусок кода на С++, который может читать произвольное местоположение памяти и вызывать это через JNI. То же самое верно для обратной записи на адрес памяти.

Напишите сначала определение класса для класса, который должен обрабатывать это, например:

public class MemTest {
    public native byte[] readMemory(int address);
    public native void writeMemory(int address, byte[] values);
}

Затем вы скомпилируете его. Затем вы используете javah.exe(или эквивалент linux) для создания заголовка для него:

javah MemTest

Теперь вы пишете файл .cpp, который включает этот заголовок и определяет методы. Компиляция с DLL. Чтобы загрузить DLL, вы либо используете параметр JDM -Djava.library.path с соответствующим значением, либо System.loadLibrary().

Обратите внимание: я не рекомендую это делать. Есть почти наверняка лучшие способы делать то, что вы хотите сделать.

Ответ 5

Distributed_cache - лучшее решение для удовлетворения ваших требований.

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

Несколько вариантов:

Terracotta позволяет потокам в кластере JVM взаимодействовать друг с другом через границы JVM, используя те же встроенные средства JVM, которые были расширены иметь общее значение кластера

Oracle_Coherence является фирменным 1 Java-сетка данных в памяти, предназначенная для повышения надежности, масштабируемости и производительности, чем традиционные системы управления реляционными базами данных.

Ehcache - широко используемый открытый Java-кеш с открытым кодом для кэширования общего назначения, Java EE и легких контейнеров. В нем хранятся хранилища памяти и дисков, копируются и недействительны, прослушиватели, загрузчики кешей, расширения кешей, обработчики исключений кеша, фильтр сервлета gzip-кеширования, API RESTful и SOAP

Redis является сервером структуры данных. Он с открытым исходным кодом, с сетевым доступом, в памяти и хранит ключи с дополнительной долговечностью.

Couchbase_Server - это многокомпонентный пакетный пакет NoSQL для документов с открытым исходным кодом (shared-no architecture), который оптимизирован для интерактивных приложений. Эти приложения могут обслуживать множество одновременных пользователей, создавая, сохраняя, извлекая, агрегируя, обрабатывая и представляя данные.

Полезные сообщения:

Что такое Терракота?

Является ли Terracotta распределенным кешем?

infoq статья

Ответ 6

Небезопасно с памятью с отключенной памятью

Как использовать Unsafe для копирования байтов объектов в зону вне зоны, а затем как передать дешевый указатель и имя класса во вторую JVM, которая будет использовать указатель и имя класса для копирования и удаления пространства с кучей объект в куче во втором JVM. Это не тот экземпляр объекта, а быстрый экземпляр без сериализации.

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

MyStructure structure = new MyStructure(); // create a test object
structure.x = 777;

long size = sizeOf(structure);
long offheapPointer = getUnsafe().allocateMemory(size);
getUnsafe().copyMemory(
            structure,      // source object
            0,              // source offset is zero - copy an entire object
            null,           // destination is specified by absolute address, so destination object is null
            offheapPointer, // destination address
            size
    ); // test object was copied to off-heap

Pointer p = new Pointer(); // Pointer is just a handler that stores address of some object
long pointerOffset = getUnsafe().objectFieldOffset(Pointer.class.getDeclaredField("pointer"));
getUnsafe().putLong(p, pointerOffset, offheapPointer); // set pointer to off-heap copy of the test object

structure.x = 222; // rewrite x value in the original object
System.out.println(  ((MyStructure)p.pointer).x  ); // prints 777

....

class Pointer {
    Object pointer;
}

Итак, теперь вы передаете MyStructure и p из ((MyStructure) p.pointer).x во вторую JVM, и вы должны иметь возможность:

MyStructure locallyImported = (MyStructure)p.pointer;

Я могу представить пример использования: предположим, что у вас есть 2 Microservices, которые могут выполняться или не работать на одном сервере, а стратегия клиента, возможно, реализована в контейнере AppServer, который знает, где развертываются службы, в случае обнаружения запрашиваемая служба находится в локальной сети, он может использовать клиент службы на основе небезопасного доступа для прозрачного запроса другой службы. Неприятный, но интересный, я хотел бы увидеть последствия для производительности использования сети, минуя WebAPI (вызывающий непосредственно обработчик), а не сериализацию. Помимо параметров контроллера в этом случае должен быть предоставлен сам контроллер. Даже не думал о безопасности.

фрагменты кода, взятые из https://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/