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に埋め込んでくれる、お約束。

今日の所は、いらいらしたので、これで御仕舞。