clojureを分解する(番外編:jdkのお勉強 -- 2)
昨日は、jdb を使って、動いている Javaのプロセスに、そのpidを指定して接続
出来ると書いたが、嘘であった。(実際にやってみると、接続を拒否される。考え
てみれば、当たり前の事で、こんな事が出来たら、大変な事になる!)
正解は、Javaを起動する時に、サーバーモードを指定してあげるのだ。
sakae@debian:~/clj$ java -Xdebug -Xrunjdwp:transport=dt_socket,address=50000,server=y,suspend=n -jar clojure.jar
Listening for transport dt_socket at address: 50000
Clojure
user=>
サーバー(Debugしたい、Javaプロセス)のポート番号を指定して、jdbを起動。
sakae@debian:~$ jdb -attach 50000
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
> list
No thread specified.
> thread main
"main" is not a valid thread id.
> threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)0x543 Reference Handler cond. waiting
(java.lang.ref.Finalizer$FinalizerThread)0x544 Finalizer cond. waiting
(java.lang.Thread)0x545 Signal Dispatcher running
Group main:
(java.lang.Thread)0x1 main running
> thread 0x1
main[1] suspend
All threads suspended.
main[1] where
[1] java.io.FileInputStream.readBytes (native method)
[2] java.io.FileInputStream.read (FileInputStream.java:199)
[3] java.io.BufferedInputStream.read1 (BufferedInputStream.java:256)
[4] java.io.BufferedInputStream.read (BufferedInputStream.java:317)
[5] sun.nio.cs.StreamDecoder.readBytes (StreamDecoder.java:264)
[6] sun.nio.cs.StreamDecoder.implRead (StreamDecoder.java:306)
[7] sun.nio.cs.StreamDecoder.read (StreamDecoder.java:158)
[8] java.io.InputStreamReader.read (InputStreamReader.java:167)
[9] java.io.BufferedReader.fill (BufferedReader.java:136)
[10] java.io.BufferedReader.read (BufferedReader.java:157)
[11] java.io.LineNumberReader.read (LineNumberReader.java:108)
[12] java.io.FilterReader.read (FilterReader.java:48)
[13] java.io.PushbackReader.read (PushbackReader.java:73)
[14] clojure.lang.LispReader.read (LispReader.java:123)
[15] clojure.core$read__3694.invoke (core.clj:1,805)
[16] clojure.core$read__3694.invoke (core.clj:1,803)
[17] clojure.main$repl__5158$fn__5164.invoke (main.clj:83)
[18] clojure.main$repl__5158$fn__5173.invoke (main.clj:97)
[19] clojure.main$repl__5158.doInvoke (main.clj:96)
[20] clojure.lang.RestFn.invoke (RestFn.java:426)
[21] clojure.main$repl_opt__5198.invoke (main.clj:153)
[22] clojure.main$_main__5222.doInvoke (main.clj:228)
[23] clojure.lang.RestFn.invoke (RestFn.java:402)
[24] clojure.lang.AFn.applyToHelper (AFn.java:172)
[25] clojure.lang.RestFn.applyTo (RestFn.java:137)
[26] clojure.main.main (null)
main[1] resume
All threads resumed.
> thread 0x1
main[1] stop in clojure.main$repl__5158$fn__5164.invoke
Set breakpoint clojure.main$repl__5158$fn__5164.invoke
main[1]
Breakpoint hit: "thread=main", clojure.main$repl__5158$fn__5164.invoke(), line=83 bci=0
main[1] list
Source file not found: main.clj
上記で仕掛けたBPは、REPLのreadに当たる部分であり、clojureからS式を入力すると
即、hit しましたよ。まあ、当たり前の事だけど、、
また、thread のスタックを見れば、呼び出し順が、一発で分かりますね。
上記ミスのお詫びと言っては、なんですが、JVMのヒープを覗ける事が分かったので
掲載しておきます。GC好きには、堪りませんね。こんなのは、aclでもいじってなけ
れば、めったにお目にかかれませんから。
sakae@debian:~/clj$ jmap -heap 2599
Attaching to process ID 2599, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 11.2-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 67108864 (64.0MB)
NewSize = 1048576 (1.0MB)
MaxNewSize = 4294901760 (4095.9375MB)
OldSize = 4194304 (4.0MB)
NewRatio = 12
SurvivorRatio = 8
PermSize = 12582912 (12.0MB)
MaxPermSize = 67108864 (64.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 983040 (0.9375MB)
used = 92616 (0.08832550048828125MB)
free = 890424 (0.8491744995117188MB)
9.42138671875% used
Eden Space:
capacity = 917504 (0.875MB)
used = 54688 (0.052154541015625MB)
free = 862816 (0.822845458984375MB)
5.960518973214286% used
From Space:
capacity = 65536 (0.0625MB)
used = 37928 (0.03617095947265625MB)
free = 27608 (0.02632904052734375MB)
57.87353515625% used
To Space:
capacity = 65536 (0.0625MB)
used = 0 (0.0MB)
free = 65536 (0.0625MB)
0.0% used
tenured generation:
capacity = 4194304 (4.0MB)
used = 721720 (0.6882858276367188MB)
free = 3472584 (3.3117141723632812MB)
17.20714569091797% used
Perm Generation:
capacity = 12582912 (12.0MB)
used = 3129216 (2.9842529296875MB)
free = 9453696 (9.0157470703125MB)
24.8687744140625% used
ここで、Sunに一言文句を言いたい。
24.8687744140625% こんなに有効桁数いらないって。精々、24.8% ぐらいで十分。
同じように、9.0157470703125MB もそうだな。Byte以下を表示しても意味なし。
大飯食らいの、Javaだったら、KB の桁で十分!!!! 9.015MB
最後に残った難物(私に取ってですが)は、ant だ。
メビンよりめふん、ant より make、rakeよりmakeが好きな私は、車輪の再発明も
ほどほどにと、声を大にして言いたい。(石器時代の類人猿が、吼えてると言われそう)
新発明のbuild.xmlを見たって、分かりそうで分からない。
sakae@debian:~/clj$ ant -f build.xml
Buildfile: build.xml
clean:
[delete] Deleting directory /home/sakae/clj/classes
init:
[mkdir] Created dir: /home/sakae/clj/classes
compile_java:
[javac] Compiling 113 source files to /home/sakae/clj/classes
[javac] Note: Some input files use unchecked or unsafe operations.
[javac] Note: Recompile with -Xlint:unchecked for details.
compile_clojure:
[java] Compiling clojure.core to /home/sakae/clj/classes
[java] Compiling clojure.main to /home/sakae/clj/classes
[java] Compiling clojure.set to /home/sakae/clj/classes
[java] Compiling clojure.xml to /home/sakae/clj/classes
[java] Compiling clojure.zip to /home/sakae/clj/classes
[java] Compiling clojure.inspector to /home/sakae/clj/classes
jar:
[jar] Building jar: /home/sakae/clj/clojure.jar
BUILD SUCCESSFUL
Total time: 1 minute 10 seconds
実行結果と、build.xmlの一部を、下記に示す。
<target name="compile_java" depends="init"
description="Compile Java sources.">
<javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes"
debug="true" target="1.5"/>
</target>
説明の後の > は、何? 気になるので 省いてみると
sakae@debian:~/clj$ ant
Buildfile: build.xml
BUILD FAILED
/home/sakae/clj/build.xml:21: Element type "target" must be followed by either attribute specifications, ">" or "/>".
怒られたよ。仰せに従って、/> にしてみると
BUILD FAILED
/home/sakae/clj/build.xml:23: The element type "project" must be terminated by the matching end-tag "".
おい、ant(xml)よ、人を、おちょくっているんかい?
結局、私の中では、> は、ネストしてるよ、とでも言う意味なんだろうなと思っておこう。
S式の方が、よっぽどか、楽だわい。
ああ、今まで何気に jdb を使ってきたけど、ここで、javac -g やってたのね。
同じようにして、*.clj のコンパイルは clojure.lang.Compile で、出来るのね。
また、jarファイルは jar コマンドを使って作る。jarファイル中に、mainを含んだ
クラスが幾つもある時、デフォで起動するクラスを Main-Classで指定しておく。
そうすると、その情報をMETA-INF/MANIFEST.MFに埋め込んでくれる、お約束。
今日の所は、いらいらしたので、これで御仕舞。