既存の秘密鍵・証明書からJavaのTrustStore & KeyStoreを作る手順メモ
すぐに忘れそうなので、メモメモ。
KeyStore
openssl pkcs12 -inkey server.key -in server.crt -export -out keystore.pkcs12 keytool -importkeystore -srckeystore keystore.pkcs12 -srcstoretype pkcs12 -destkeystore keystore.jks -destkeypass keypassword -deststorepass storepassword # 内容を確認 keytool -list -v -keystore keystore.jks
TrustStore
keytool -import -file server.crt -alias mytruststore -keystore truststore.jks # 内容を確認 keytool -list -v -keystore truststore.jks
IntelliJでRobocodeのRobot開発メモ
毎回忘れて色々試しては時間を食っている気がするのでメモ。
1. Robocode自体のインストール (See http://robowiki.net/wiki/Robocode/Download)
2. IntelliJで適当にprojectを作り、`Project Settings` -> `Modules (Dependencies)` に `JARs or directory` として上記1でインストールしたRobocode配下にある `libs/robocode.jar` を追加
3. http://robowiki.net/wiki/Robocode/My_First_Robot 辺りを参考に適当なRobotを作成
4. IntelliJ上で `Build Project` しておき class files を出力させておく
5. Robocodeの `Options` -> `Preferences` -> `Development Options` に上記4の class files が出力されている root directory を指定
6. あとは普通にRobocode上の `New` 以降で作成した Robot が指定できるようになっているはず
JVM Tool Interfaceを少しさわってみたメモ
ふと、JVM(TM) Tool Interface 1.2.3 に触れたことがないことに気がつき、少しさわってみたのでメモ。
OpenJDKのソースコードの jdk/src/share/demo/jvmti の下に幾つか例があるので、それを試してみることに。ちなみに概要は上記URLのドキュメントの各APIの説明の直前まで眺めれば良さそう。APIの種類が多いので少し圧倒されるけど、実際に何か作る際に目的に合ったものを見れば問題無いかと。
今回見てみたのは versionCheck / mtrace の二つ。
- versionCheck
Agent_OnLoad()でJVMTI_EVENT_VM_INITイベントにコールバック関数をセットして、その中でjvmtiEnvから引っこ抜いたバージョン番号をstdoutに表示するだけのシンプルなもの
Mac OS X上でのビルドは以下のような感じ。ちなみにagent_util/agent_util.cはjdk/src/share/demo/jvmtiが依存しているヘルパー関数群。
$ pwd /Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/versionCheck $ clang -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin -I../agent_util -c ../agent_util/agent_util.c *.c $ clang -dynamiclib -L${JAVA_HOME} -o libversionCheck.so *.o
これを利用する場合は以下のように指定する
$ java -agentpath:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/versionCheck/libversionCheck.so Main Compile Time JVMTI Version: 1.2.1 (0x30010201) Run Time JVMTI Version: 1.2.3 (0x30010203) Hello world
- mtrace
Javaのメソッド呼び出し回数をトレースするもの。Agent_OnLoad()で色々コールバックを設定するが、面白いのはJVMTI_EVENT_CLASS_FILE_LOAD_HOOKで都度ロードされたクラスファイルの各メソッドを、同梱されているMtrace.javaのメソッドを最初に呼ぶように書き換えている(と思う)。Mtrace.javaのメソッドは当該Cライブラリファイルの関数(この中でメソッド呼び出し回数をカウント)をnative methodとして呼ぶように紐づけられているが、その際、JNIのFindClass()とRegisterNative()を使っている。ちなみにjava_crw_demo/java_crw_demo.c はクラスファイル操作系ヘルパー関数群。
$ pwd /Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace $ clang -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin -I../agent_util -I../java_crw_demo -c ../agent_util/agent_util.c ../java_crw_demo/java_crw_demo.c *.c $ clang -dynamiclib -L${JAVA_HOME} -o libmtrace.so *.o $ mkdir -p classes $ javac -d classes Mtrace.java $ jar cf mtrace.jar *
使い方で少しハマった点としては、FindClass()でMtrace classを探す際、ロードされる前に探しに行くとJVMがクラッシュしてしまう(FindClass()の戻り値がNULLになることを期待してたけど...)。なので -Xbootclasspath/a でmtrace.jarを先にロードする必要あり
$ java -Xbootclasspath/a:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace/classes/mtrace.jar -agentpath:/Users/komamitsu/src/openjdk/jdk/src/share/demo/jvmti/mtrace/libmtrace.so Main : Class java/lang/String 8084 calls Method charAt (I)C 3941 calls 3941 returns Method length ()I 832 calls 832 returns Method equals (Ljava/lang/Object;)Z 543 calls 543 returns :
なお、途中で、調査のため-Xcheck:jni -verboseを有効にしたら捗った。特に-verbose指定時に以下のエラーが出たのでJDKのバージョン違いに気がつけた(途中JDK8 -> 7にして試してた)
[Loaded java.lang.ClassFormatError from /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.UnsupportedClassVersionError from /Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home/jre/lib/rt.jar]
MessagePack v07とJacksonの文字列を対象とした性能比較
int cnt = 800000; { ObjectMapper mapper = new ObjectMapper(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); List<String> strList = new ArrayList<String>(cnt); for (int i = 0; i < cnt; i++) { strList.add("01234567"); } System.gc(); Thread.sleep(3000); long start = System.currentTimeMillis(); mapper.writeValue(outputStream, strList); System.out.println("Jackson(Serialize, String): " + (System.currentTimeMillis() - start) + "ms"); System.gc(); Thread.sleep(3000); start = System.currentTimeMillis(); mapper.readValue(outputStream.toByteArray(), new TypeReference<List<String>>() {}); System.out.println("Jackson(Deserialize, String): " + (System.currentTimeMillis() - start) + "ms"); } { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); MessagePacker packer = org.msgpack.core.MessagePack.newDefaultPacker(outputStream); System.gc(); Thread.sleep(3000); long start = System.currentTimeMillis(); for (int i = 0; i < cnt; i++) { packer.packString("01234567"); } packer.flush(); System.out.println("MessagePack(Serialize, String): " + (System.currentTimeMillis() - start) + "ms"); MessageUnpacker unpacker = org.msgpack.core.MessagePack.newDefaultUnpacker(outputStream.toByteArray()); System.gc(); Thread.sleep(3000); start = System.currentTimeMillis(); for (int i = 0; i < cnt; i++) { unpacker.unpackString(); } System.out.println("MessagePack(Deserialize, String): " + (System.currentTimeMillis() - start) + "ms"); }
int cnt = 1000000;
Jackson(Serialize, String): 71ms Jackson(Deserialize, String): 60ms MessagePack(Serialize, String): 96ms MessagePack(Deserialize, String): 82ms
int cnt = 1000000;
Jackson(Serialize, String): 103ms Jackson(Deserialize, String): 246ms MessagePack(Serialize, String): 192ms MessagePack(Deserialize, String): 158ms
int cnt = 10000000;
Jackson(Serialize, String): 574ms Jackson(Deserialize, String): 7259ms MessagePack(Serialize, String): 1316ms MessagePack(Deserialize, String): 1049ms
ByteBufferへのコピー速度比較
JavaでByteBufferへのコピーを行う際の速度を簡単に比較してみました。java.nio.ByteBuffer#put(byte)で1byteずつ書き込んでいくのでなければ大体同じですね。
Heap上のbyte arrayをコピー
java.nio.ByteBuffer#put(byte[], int, int)
byte[] bs = new byte[256 * 1024 * 1024]; ByteBuffer src = ByteBuffer.wrap(bs); ByteBuffer dst = ByteBuffer.allocate(bs.length); long start = System.currentTimeMillis(); dst.put(src.array(), 0, src.remaining()); System.out.println("heap.byte_array: ByteBuffer.put(byte[], int, int): " + (System.currentTimeMillis() - start));
=> heap.byte_array: ByteBuffer.put(byte[], int, int): 73
java.nio.ByteBuffer#put(java.nio.ByteBuffer)
dst.put(src);
System.out.println("heap.byte_array: ByteBuffer.put(java.nio.ByteBuffer): " + (System.currentTimeMillis() - start));
=> heap.byte_array: ByteBuffer.put(java.nio.ByteBuffer): 69
java.nio.ByteBuffer#put(byte) with loop
for (int i = 0; i < src.remaining(); i++) dst.put(src.get()); System.out.println("heap.byte_array: ByteBuffer.put(byte): " + (System.currentTimeMillis() - start));
=> heap.byte_array: ByteBuffer.put(byte): 380
Heap上のByteBufferをコピー
java.nio.ByteBuffer#put(byte[], int, int)
ByteBuffer src = ByteBuffer.allocate(256 * 1024 * 1024); ByteBuffer dst = ByteBuffer.allocate(256 * 1024 * 1024); long start = System.currentTimeMillis(); dst.put(src.array(), 0, src.remaining()); System.out.println("heap.bb: ByteBuffer.put(byte[], int, int): " + (System.currentTimeMillis() - start));
=> heap.bb: ByteBuffer.put(byte[], int, int): 72
java.nio.ByteBuffer#put(java.nio.ByteBuffer)
dst.put(src);
System.out.println("heap.bb: ByteBuffer.put(java.nio.ByteBuffer): " + (System.currentTimeMillis() - start));
=> heap.bb: ByteBuffer.put(java.nio.ByteBuffer): 70
java.nio.ByteBuffer#put(byte) with loop
for (int i = 0; i < src.remaining(); i++) dst.put(src.get()); System.out.println("heap.bb: ByteBuffer.put(byte): " + (System.currentTimeMillis() - start));
Off heap上のByteBufferをコピー
java.nio.ByteBuffer#put(byte[], int, int)
ByteBuffer src = ByteBuffer.allocateDirect(256 * 1024 * 1024); ByteBuffer dst = ByteBuffer.allocate(256 * 1024 * 1024); long start = System.currentTimeMillis(); dst.put(src.array(), 0, src.remaining()); System.out.println("off-heap.bb: ByteBuffer.put(byte[], int, int): " + (System.currentTimeMillis() - start));
=> java.lang.UnsupportedOperationException (it doesn't have array inside)
java.nio.ByteBuffer#put(java.nio.ByteBuffer)
dst.put(src);
System.out.println("off-heap.bb: ByteBuffer.put(java.nio.ByteBuffer): " + (System.currentTimeMillis() - start));
=> off-heap.bb: ByteBuffer.put(java.nio.ByteBuffer): 72
java.nio.ByteBuffer#put(byte) with loop
for (int i = 0; i < src.remaining(); i++) dst.put(src.get()); System.out.println("off-heap.bb: ByteBuffer.put(byte): " + (System.currentTimeMillis() - start));
=> off-heap.bb: ByteBuffer.put(byte): 358
TypeProfileが取得される場合、所得されない場合について
JavaではJITコンパイルに関連するTypeProfileという機構がありますが、これがどのような状況で取得されるのかちょっと興味があったので簡単に試してみました。
komamitsu/TypeProfileTest · GitHub
Childというinterfaceを継承するChildImplA/Bという二つのclassがあり、MainXxxxというclassからChild/ChildImplA/Bの扱い方を微妙に変えて、TypeProfileが取得される挙動を見てみました。
https://github.com/komamitsu/TypeProfileTest/tree/master/org/komamitsu/tptest にMainXxxx.java群が置いてありますが概要から言うと、"あるメソッド内で、あるオブジェクトが `new` によって生成されて、そのオブジェクトが当該メソッド内のlocal変数経由で呼ばれる場合のみTypeProfileが取得されない" ように見えました。具体的には以下です。
package org.komamitsu.tptest; import org.komamitsu.tptest.child.Child; import org.komamitsu.tptest.child.ChildImplA; import org.komamitsu.tptest.child.ChildImplB; public class MainWithoutFactoryMethodAndImportingB { public static void main(String argv[]) { Child c = new ChildImplA(); for (int i = 0; i < 10000000; i++) { c.exec(); } System.out.println(c.getCount()); } }
TypeProfileTest/WithoutFactoryMethodAndImportingB at master · komamitsu/TypeProfileTest · GitHub
$ java -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining org.komamitsu.tptest.MainWithoutFactoryMethodAndImportingB 62 1 b java.lang.String::hashCode (55 bytes) 72 2 b java.lang.String::indexOf (70 bytes) @ 66 java.lang.String::indexOfSupplementary (71 bytes) too big 82 3 b org.komamitsu.tptest.child.ChildImplA::exec (11 bytes) 83 4 % b org.komamitsu.tptest.MainWithoutFactoryMethodAndImportingB::main @ 10 (41 bytes) @ 17 org.komamitsu.tptest.child.ChildImplA::exec (11 bytes) inline (hot) 85 4 % org.komamitsu.tptest.MainWithoutFactoryMethodAndImportingB::main @ -2 (41 bytes) made not entrant 10000000
もうちょっと色々なケースで試してみたけれど、ひとまずはここまでで。
JavaのEscape AnalysisでNoEscapeがGlobalEscapeになってしまうケースの検証
Java ™ HotSpot Virtual Machine Performance Enhancements
Escape analysis is supported and enabled by default in Java SE 6u23 and later.
ということで最近のJVMでは有効になっているEscape analysisですが、こんな記事を見つけました。
Richard Burnison - Escape Analysis & Stack Allocated Objects
NoEscapeについて説明しているくだりで
Important to note, however, is that objects requiring finalizer execution are considered GlobalEscape and will not be stack-bound.
という記述があり、ちょっと気になったので試してみました。
$ java -version java version "1.7.0_45" Java(TM) SE Runtime Environment (build 1.7.0_45-b18) Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
まずは普通に。
public class Main { void run() { int totalLen = 0; for (int i = 0; i < 100000000; i++) { String s = new String("hello"); totalLen += s.length(); } System.out.println(totalLen); } public static void main(String argv[]) { Main main = new Main(); main.run(); } }
- Escape analysis無し
$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:-DoEscapeAnalysis Main [GC [PSYoungGen: 132096K->432K(153600K)] 132096K->440K(503296K), 0.0030680 secs] [Times: user=0.00 sys=0.01, real=0.01 secs] 50000000 Heap PSYoungGen total 153600K, used 111393K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000) eden space 132096K, 84% used [0x00000007f5500000,0x00000007fc15c698,0x00000007fd600000) from space 21504K, 2% used [0x00000007fd600000,0x00000007fd66c010,0x00000007feb00000) to space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000) ParOldGen total 349696K, used 8K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000) object space 349696K, 0% used [0x00000007dff80000,0x00000007dff82000,0x00000007f5500000) PSPermGen total 21504K, used 2554K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000) object space 21504K, 11% used [0x00000007dad80000,0x00000007daffebf8,0x00000007dc280000)
- Escape analysis有り
$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:+DoEscapeAnalysis Main 50000000 Heap PSYoungGen total 153600K, used 5284K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000) eden space 132096K, 4% used [0x00000007f5500000,0x00000007f5a29028,0x00000007fd600000) from space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000) to space 21504K, 0% used [0x00000007fd600000,0x00000007fd600000,0x00000007feb00000) ParOldGen total 349696K, used 0K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000) object space 349696K, 0% used [0x00000007dff80000,0x00000007dff80000,0x00000007f5500000) PSPermGen total 21504K, used 2552K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000) object space 21504K, 11% used [0x00000007dad80000,0x00000007daffe060,0x00000007dc280000)
Escape analysis有りの場合、Eden領域の使用量が少なくHeapではなくStack allocationになっているものと推測。
では、finalize() 実装版
public class Main { static int finalizeCount = 0; void run() { int totalLen = 0; for (int i = 0; i < 10000000; i++) { String s = new String("hello"); totalLen += s.length(); } System.out.println(totalLen); System.out.println(finalizeCount); } @Override protected void finalize() throws Throwable { finalizeCount++; super.finalize(); } public static void main(String argv[]) { Main main = new Main(); main.run(); } }
これをEA有効にして実行してみると
$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:+DoEscapeAnalysis Main 50000000 0 Heap PSYoungGen total 153600K, used 5284K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000) eden space 132096K, 4% used [0x00000007f5500000,0x00000007f5a29028,0x00000007fd600000) from space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000) to space 21504K, 0% used [0x00000007fd600000,0x00000007fd600000,0x00000007feb00000) ParOldGen total 349696K, used 0K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000) object space 349696K, 0% used [0x00000007dff80000,0x00000007dff80000,0x00000007f5500000) PSPermGen total 21504K, used 2552K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000) object space 21504K, 11% used [0x00000007dad80000,0x00000007daffe2f0,0x00000007dc280000)
と、finalize() 実装前と変わらないので、"objects requiring finalizer execution are considered GlobalEscape and will not be stack-bound" というのは(少なくとも1.7.0_45では)気にしなくて良さそうかなぁと思います。