web-dev-qa-db-ja.com

2つのJVM間の共有メモリ

Javaで、2つのJVM(同じ物理マシン上で実行)が同じメモリアドレススペースを使用/共有する方法はありますか? JVM1のプロデューサーが特定の事前定義されたメモリ位置にメッセージを置くと仮定します。JVM2のコンシューマは、どのメモリ位置を調べるべきかを知っている場合、メッセージを取得できますか?

30
OneWorld

ソリューション1:

私の意見では、メモリマップファイルを使用するのが最善の解決策です。これにより、他の非Javaプログラムを含む、任意の数のプロセス間でメモリ領域を共有できます。マップされたメモリにJavaオブジェクトを配置することはできません次の例は、2つの異なるプロセス間で通信できることを示していますが、プロセス間の通信を改善するには、さらに洗練させる必要があります。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 を使用してプロセス間でやり取りすることです。これには、ネットワークを介した通信が非常に簡単にできるという利点があります。これは、メモリマップファイルを使用するよりも遅くなりますが、そのステートメントをバックアップするベンチマークがありません。信頼できるネットワークプロトコルを実装するのは非常に複雑になる可能性があり、アプリケーション固有のコードなので、クイック検索で見つけることができる多くの優れたネットワークサイトがあります。


2つの異なるプロセス間でメモリを共有する場合の上記の例です。現在のプロセスで任意のメモリを読み書きしたいだけの場合、最初に知っておくべき警告がいくつかあります。これは、JVMの原則全体に反するため、実際に運用コードでこれを行うべきではありません。すべての安全性に違反しており、注意を怠るとJVMを簡単にクラッシュさせる可能性があります。

とはいえ、試してみるのはとても楽しいです。現在のプロセスの任意のメモリを読み書きするには、Sun.misc.Unsafeクラス。これは、私が認識し使用しているすべてのJVMで提供されます。クラスの使用方法の例は here にあります。

28
Smith_61

Javaのメモリマップファイルを介した共有メモリの使用を容易にするいくつかのIPCライブラリがあります。

クロニクルキュー

Chronicle Queueは非ブロッキングJava Queueと似ていますが、1つのJVMでメッセージを提供し、別のJVMでポーリングできることを除きます。

両方のJVMで、同じFS=ディレクトリにChronicleQueueインスタンスを作成する必要があります(このディレクトリは、メモリにマウントされたFS 「メッセージの永続化は必要ありません」:

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

1つの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機能も提供します。ChronicleQueueに似ています。重要な違いの1つは、メッセージのマーシャリング/デマーシャリングに [〜#〜] sbe [〜#〜] ライブラリを使用し、Chronicle Queueが 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"));

次に、1つのJVMで次のように記述できます。

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

受信側のJVMで:

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

Distributed_cache は、要件に対処する最適なソリューションです。

コンピューティングでは、分散キャッシュは、単一のロケールで使用される従来のキャッシュの概念を拡張したものです。分散キャッシュは複数のサーバーにまたがることができるため、サイズと国境を越えた容量を増やすことができます。

いくつかのオプション:

Terracotta は、JVMのクラスター内のスレッドが、クラスター全体の意味を持つように拡張された同じ組み込みJVM機能を使用して、JVM境界を越えて相互に対話できるようにします。

Oracle_Coherence は独自仕様です 1 従来のリレーショナルデータベース管理システムよりも信頼性、スケーラビリティ、パフォーマンスが向上するように設計されたJavaベースのメモリ内データグリッド

Ehcache は広く使用されているオープンソースですJava汎用キャッシング用の分散キャッシュ、Java EEおよび軽量コンテナ。メモリおよびディスクストア、コピーおよび無効化による複製、リスナー、キャッシュローダー、キャッシュ拡張、キャッシュ例外ハンドラー、gzipキャッシングサーブレットフィルター、RESTfulおよびSOAP API

Redis はデータ構造サーバーです。オープンソースで、ネットワーク化され、メモリ内にあり、オプションで耐久性のあるキーを保存します。

Couchbase_Server は、対話型アプリケーション向けに最適化された、オープンソースの分散(シェアードナッシングアーキテクチャ)マルチモデルNoSQLドキュメント指向データベースソフトウェアパッケージです。これらのアプリケーションは、データを作成、保存、取得、集約、操作、および提示することにより、多くの同時ユーザーにサービスを提供できます。

便利な投稿:

テラコッタとは?

Terracottaは分散キャッシュですか?

infoq 記事

5
Ravindra babu

正直なところ、同じメモリを共有したくありません。必要なデータのみを他のJVMに送信する必要があります。そうは言っても、共有メモリが必要な場合doには、他のソリューションが存在します。

データの送信2つのJVMは同じメモリアクセスポイントを共有しないため、あるJVMからの参照を別のJVMで使用することはできません。彼らはお互いを知らないので、新しい参照は単に作成されます。

ただし、他のJVMにデータを出荷し、さまざまな方法で戻すことができます。

1) [〜#〜] rmi [〜#〜] を使用して、データを解析するリモートサーバーをセットアップできます。セキュリティの変更が必要であり、データがSerializableであるため、設定するのは少し面倒です。リンクで詳細を確認できます。

2)サーバーを使用することは、さまざまな場所にデータを送信する昔からの方法です。これを実装する1つの方法は、 ServerSocket を使用し、Socketlocalhost を使用して接続することです。 Serializable を使用する場合、オブジェクトはObjectOutputStreamである必要があります。


データの共有これは非常に危険で揮発性があり、低レベルであり、安全ではありません(文字通り)。

Javaコードを使用する場合は、 s.m.Unsafe 、正しいメモリアドレスを使用すると、OSのバッキングC/C++配列によって保存されたオブジェクトを取得できます。

それ以外の場合は、nativeメソッドを使用してC/C++配列に自分でアクセスできますが、これを実装する方法はわかりません。

4
Breeze

はい、

中間プログラムを使用すると、任意のメモリ位置への書き込みと読み取りができます。 Javaだけではできません。

たとえば、任意のメモリ位置を読み取り、JNI経由で呼び出すことができるC++コードを作成できます。メモリアドレスに書き込む場合も同様です。

これを処理するクラスのクラス定義を最初に記述します。次に例を示します。

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をロードするには、適切な値で-Djava.library.path JVMパラメーターを使用するか、System.loadLibrary()を使用します。

注意事項:これを行うことはお勧めしません。あなたがやりたいことをするより良い方法はほぼ確実にあります。

2
Xabster

Jocket 、私が数年前に作った実験プロジェクトはまさにこれを行います。

Java.net.Socketを使用する場合は、Java.net.ServerSocketおよびInput/OutputStreamのドロップイン置換が含まれます。

各方向チャネルは、循環バッファのペアを使用して、データをポストおよび取得します(1つは「パケット」用、もう1つはパケットのアドレス用)。バッファーはRandomAccessFileを介して取得されます。

IPC同期(つまり、データの可用性を他のプロセスに通知する)を実装する小さなJNIレイヤー(Linux)が含まれていますが、データをポーリングする場合は必須ではありません。

1
pcdv

ピボットオフヒープメモリでは安全ではありません

Unsafeを使用してオブジェクトバイトをオフヘッドゾーンにコピーしてから、ポインターとクラス名を使用してオフヒープスペースをコピーし、インヒープにキャストする2番目のJVMに安価なポインターとクラス名を渡す方法2番目の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.pointer).xからMyStructurepを2番目のJVMに渡すと、次のことができるはずです。

MyStructure locallyImported = (MyStructure)p.pointer;

ユースケースを想像できます:同じサーバーで実行されている場合とされていない場合がある2つのマイクロサービスと、要求されたサービスを検出した場合にサービスがデプロイされる場所を知っているコンテナAppServerに実装されたクライアント戦略があるとしますローカルにある場合、Unsafeベースのサービスクライアントを使用して、他のサービスを透過的に照会できます。厄介ですが興味深いのは、ネットワークを使用しないこと、WebAPIをバイパスすること(コントローラーを直接処理することを呼び出すこと)、およびシリアル化しないことのパフォーマンスへの影響を確認することです。この場合のコントローラーパラメーターとは別に、コントローラー自体を提供する必要があります。セキュリティについても考えませんでした。

https://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-Java/ から借用したコードスニペット

0
juanmf