ラベル java の投稿を表示しています。 すべての投稿を表示
ラベル java の投稿を表示しています。 すべての投稿を表示

2012年8月17日金曜日

JavaからJNI経由で共有メモリ for Win

javaからJNIで共有メモリを弄りたい。
そんな、ひと夏の衝動にかられて、JNIで共有メモリをアクセスしてみました。
https://github.com/tkymism/sharedmem

今回も流れるインタフェースを駆使して、こんな感じのインタフェースにしてみた訳です。



使い方
(1)VisualStudioでdll生成

  •   構成プロパティ➡C/C++-➡全般➡追加のインクルードディレクトリ
    • [jdk_dir]\include;
    • [jdk_dir]\include\win32;
    • [git_dir]\sharedmem\sharedmem-win\include
  •   構成プロパティ➡C/C++➡コード生成➡ランタイムライブラリ
    • マルチスレッド (/MT)
  •  構成プロパティ➡リンカー➡全般➡追加のライブラリディレクトリ
    • [jdk_dir]\lib;
  •  構成プロパティ➡リンカー➡入力➡追加の依存ファイル
    • jvm.lib;

(2)maven installでjar生成


解説
"hogehoge"という名前で共有メモリに書き込んでいます。0~99byteはヘッダーとして使用して、100byte目以降を実態のファイルとしてallocateする図です。
locateでは0~99byte目にアクセスしてByteBuffer(Direct)で共有メモリに直接attachします。 100byte目以降(locate)にもdataを突っ込んで、そしてreleaseする訳です。
やはり、共有メモリに対してアクセスするためには排他制御が必要です。今回は読み込みと書き込み両方でMutexを使って排他制御を行っています。
このプログラムではMutexの名前には"<共有メモリ名>+.lock"で生成します。
SharedMemoryRepository#readonly(String name)排他する
SharedMemoryRepository#writable(String name)排他する
SharedMemoryRepository#copy(String name)排他しない
WindowsAPIのOpenFileMappingとMapViewOfFileでは読み込みモードが一緒じゃないとアクセス権でエラーが出ました。なので、以下のようにopenした段階でモードを決定してmappingのモードと同じになるようにしています。 ByteBufferは非常に便利なんですが、共有メモリにjavaのObjectをserializedしたbyte配列を格納したいと思い立ったため、こんなインタフェースも追加してみました。

課題
NativeAPIを触るとやはりallocateの煩わしさにいらだちます。allocateする際にbyte列を指定するのですが、CreateFileMapping関数ではサイズを64bitで指定できるのですが、32bitづつ分けて設定してくれという大胆な関数でした。 そんな変態的な仕様に対して、javaからはlong(8byte)をint(4byte)に分割して状態でJNIに引き渡すようにしてみた訳ですが、本当に正しいかが疑問に残ります。 ほかにも色々課題はあるのですが、とりあえずこれでcommit.

共有メモリを使う動機
私の部署で開発しているパッケージは基本的にjava(Swing)で開発している訳ですが、既存製品から移行し続ける経緯もあり、未だに帳票処理やバッチ処理ではCOBOLで開発しています。旧モデルの保守性を考えると、ビジネスロジック(COBOL)の資産がそのまま残ってしまったということです。
さて、javaからCOBOLを呼び出す際にはJava->JNI(C)->COBOLという破廉恥な連携が行われている訳ですが、たまにヘタクソなコボラーさんが、添字エラーを起こしちゃうようなプログラムを作っちゃうと、javaごとプロセスが死んでしまうという大惨事が引き起こされます。
なので、COBOLの処理をなんとかして別プロセスで動作させたいという動機が生まれた訳です。
でも、javaで抱えるメモリは共有したい!ということで、Google App EngineなんかであるようなMemcacheを、Windowsの共有メモリを使って実現したいということになったわけです。

2012年8月13日月曜日

JNIでヒープ外を参照


JNIを使ってヒープ外のメモリにアクセスしていたわけで。そしたら、実装方法により性能が全然違ったのでメモ。



ByteBufferでアクセス

ByteBufferクラスを使ってアクセスするのが最高性能が出ました。以下に実装例を記載。
Java
C++(MSVC)

byte[]でアクセス

ByteBufferより倍の性能かかった実装例で、byte[]を引き渡す方式。C側でJavaの配列操作をするのに時間がかかるようです。
Java
C++(MSVC)



1 byteずつアクセス

最初はbyte数分、Java側でループを行って1byteずつ参照する方式。JNIのオーバヘッドが大きいようで、ByteBufferクラスに比べ100倍以上の時間がかかっていた。
Java
C++(MSVC)

2012年8月11日土曜日

Java外部起動

最近、Windowsの共有メモリ(BaseNamedObjects)をJavaでアクセスするプログラムを書いているのだけど、そうすると別プロセスの起動をJUnitで書きたくなる訳で。
Javaの別プロセスを起動する場合にこんな感じで書けたら良いなと思いながら書いたものをgithubに公開してみる、そんな夕下がり。
https://github.com/tkymism/java-launch

ここでのポイントは標準出力を親プロセスに引き渡すJavaMainクラス。(参考:ひしだま's 技術メモページ)
インタフェースは流してみる。
Linuxは現在、未対応でMacとWindowsで動作確認済。
標準出力で文字コードを正しく読み取るためには親プロセスと子プロセスで文字コードを合わせる必要があるようです。


2010年11月25日木曜日

デザインパターン

デザインパターンはとは、書籍「オブジェクト指向における再利用のためのデザインパターン」において、所謂GoFと呼ばれる先人たちが、ソフトウェアに導入にしたオブジェクト指向におけるプログラム設計のパターンです。
これらのデザインパターンを使用してクラス設計することで、開発チーム内の用語統一化が進み、意思統一が図りやすくなります。
GoFのデザインパターンでは、23のデザインパターンが用意されています。

生成に関するパターン
  • Singletonパターン
  • FactoryMethodパターン
  • AbstractFactoryパターン
  • Builderパターン
  • Prototype パターン
構造に関するパターン
  • Adapterパターン
  • Bridgeパターン
  • Compositeパターン
  • Decoratorパターン
  • Facadeパターン
  • Flyweightパターン
  • Proxyパターン
振る舞いに関するパターン
  • ChainOfResponsibilityパターン
  • Commandパターン
  • Interpreterパターン
  • Iteratorパターン
  • Mediatorパターン
  • Mementoパターン
  • Observerパターン
  • Stateパターン
  • Strategyパターン
  • TemplateMethod パターン
  • Visitorパターン

Singletonパターン

Singletonパターン(シングルトン)を使用する場合、そのクラスのインスタンスが1つしか生成されないことを保証することができます。
Singletonパターンでは、様々なクラスから呼び出しが可能となります。
ただし、ステートフルに実装してしまうと、グローバル変数のように、プログラムの依存性が煩雑化し、構造が悪化するケースがあります。
安易にシングルトン化することは、非常に危険な行為ですので、十分に考慮する必要があります。



実装例
  • コンストラクタはprivateで定義する。
  • staticで、かつprivateで自分のインスタンスをもつ。
  • staticメソッドでgetInstance()メソッドを定義する。




public class MySingleton {
private static final MySingleton singleton = new MySingleton();
private MySingleton(){}
public static MySingleton getInstance(){
return singleton;
}
}




誤った実装例

以前、以下のような中途半端なSingletonを見たことがあります。



public class MySingleton {
private static MySingleton singleton;
private MySingleton(){}
public static MySingleton getInstance(){
if(singleton == null){
singleton = new MySingleton();
}
return singleton;
}
}


上記の実装を行った場合、getInstance()が非同期呼び出しに対応できていません。
そのため、getInstance()をsynchronizedする必要が出てきます。もし、synchronizedした場合、呼び出し単位で同期をとるため、性能への影響が発生します。