Java

PhantomReference

Java NetBeans

お正月休みも今日で終わってしまいます。

何処にも出かけず家でごろごろしていました。(^_^;)

居場所に困るのでこうやってパソコンでプログラムを組んで時間を無駄に使ってます。

今日は PhantomReference を試してみます。

その前に前回、前々回と PhantomReference の兄弟の SoftReferenceWeakReference の御浚いをしておきましょう。

SoftReference はリファレントがソフト到達可能となっても使用頻度とヒープの状況から、ガーベージ・コレクタがリファレントを解放すべきだと判断するまで GC 対象にならない。

Weak Reference はリファレントが弱到達可能となったら GC 対象となる。

言葉での説明より簡単なプログラムと組んで実行してみたほうが理解しやすいです。

では、非常に解りやすいプログラムを組んでみました。

プログラムの中にいる”ぼく”はあなたの場合、SoftReference タイプでしょうか?

それとも Weak Reference タイプでしょうか?

 

 

このプログラムの実行結果はつぎのようになります。

僕は二人の素敵な女性を愛してしまった。
僕はガッキーを愛してる。
僕はすずを愛してる。
— 僕からすずへの愛を無くした。 —
ごめんね。ガッキー・・・ 僕のすずへの愛はまだ完全に消えていない。
ガッキー、今までごめんね。僕のすずへの愛は完全に消えた。
これからはガッキーお前だけを愛する!

次は Weak Reference を使ったプログラムです。

 

このプログラムの実行結果はつぎのようになります。

僕は二人の素敵な女性を愛してしまった。
僕はガッキーを愛してる。
僕はすずを愛してる。
— 僕からすずへの愛を無くした。 —
僕のすずへの愛は完全に消えた。 ガッキー、愛してる!

プログラムの詳細はコメントと前回、前々回のエントリーを参照してくださいませ。

さて、あなたはどちらのタイプでしたでしょう?

それでは PhantomReference を試してみましょう!

PhantomReference を使うには モジュール java.base の java.lang.Object java.lang.ref.ReferenceQueue<T> クラスが必要となります。

そういえば、モジュール java.base の java.lang.Object java.lang.ref.Reference<T> のことも忘れていたのでこれらを先にざっくりと調べます。

参照オブジェクトための抽象基底クラスである Reference<T> クラスから調べます。

クラスReference<T>

このクラスは、すべての参照オブジェクトに対して共通のオペレーションを定義します。

参照オブジェクトはガベージ・コレクタと密接に連携して実装されるので、このクラスを直接サブクラス化することはできません。

メソッドは6個あります。

public void clear()

この参照オブジェクトをクリアします。 このメソッドを呼び出しても、このオブジェクトはキューに入りません。

このメソッドはJavaコードによってのみ呼び出されます。ガベージ・コレクタが参照をクリアするときは、このメソッドを呼び出さずに直接行います。

protected Object clone()throws CloneNotSupportedException

常時 CloneNotSupportedExceptionをスローします。 Referenceは、効果的にクローニングできません。 かわりに新規のReferenceを構築します

public boolean enqueue()

この参照オブジェクトをクリアし、それが登録されているキューに登録オブジェクトを追加します。

このメソッドはJavaコードによってのみ呼び出されます。ガベージ・コレクタが参照をキューに入れるときは、このメソッドを呼び出さずに直接行います。

戻り値:この参照オブジェクトがキューに入れられた場合はtrue。すでにキューに入れられているか、作成時にキューに登録されなかった場合はfalse

public T get()

参照オブジェクトのリファレントを返します。 プログラムまたはガベージ・コレクタによって、この参照オブジェクトがすでにクリアされている場合、このメソッドはnullを返します。

戻り値:この参照が表すオブジェクト。この参照オブジェクトがクリアされている場合はnull

public boolean isEnqueued()

この参照オブジェクトが、プログラムまたはガベージ・コレクタによってキューに入れられているかどうかを判定します。

この参照オブジェクトが生成されたときにキューに登録されていない場合、このメソッドは常にfalseを返します。

戻り値:この参照オブジェクトがキューに入れられている場合にだけtrue

public static void reachabilityFence​(Object ref)

指定された参照によって参照されるオブジェクトが、オブジェクトが到達不能になる可能性のあるプログラムの以前のアクションに関係なく、「強く到達可能な」のままであることを保証します。

このため、参照されたオブジェクトは、少なくともこのメソッドが呼び出されるまでガベージ・コレクションによって再要求できません

次はモジュール java.base の java.lang.Object java.lang.ref.ReferenceQueue<T> を調べてみます。

クラスReferenceQueue<T>

参照キューです。到達可能性が適切に変更されたことが検出されると、登録されている参照オブジェクトはガベージ・コレクタによって参照キューに追加されます。

コンストラクタは引数の無いものが一つだけあります。

public ReferenceQueue()

新しい参照オブジェクト・キューを構築します。

メソッドは3個あります。

public Reference<? extends T> poll()

このキューをポーリングして、参照オブジェクトが利用可能かどうかを確認します。

参照オブジェクトが遅延なしで利用可能な場合、それがキューから削除されて、返されます。 それ以外の場合、このメソッドは即座にnullを返します。

戻り値:利用可能な参照オブジェクトがあった場合はその参照オブジェクト、そうでない場合はnull

public Reference<? extends T> remove()throws InterruptedException

このキューの次の参照オブジェクトを削除します。参照オブジェクトが利用可能になるまでブロックされます。

戻り値:参照オブジェクト。1つが利用可能になるまでブロックを行う

例外:InterruptedException – 待機中に割り込まれた場合

public Reference<? extends T> remove​(long timeout)throws IllegalArgumentException, InterruptedException

このキューの次の参照オブジェクトを削除します。参照オブジェクトが利用可能になるか、指定されたタイム・アウトの期限が切れるまでブロックします。

このメソッドはリアルタイム保証を行いません。Object.wait(long)メソッドを呼び出した場合と同様にタイム・アウトのスケジュールを作成します。

パラメータ:timeout – 値が正の場合、このキューに参照が追加されるのを待つ間、timeoutミリ秒の間ブロックされる。 0の場合、無期限にブロックされる。

戻り値:指定されたタイム・アウト期間に利用可能になった場合は参照オブジェクト、そうでない場合はnull

例外:IllegalArgumentException – timeout引数の値が負の場合
InterruptedException – タイム・アウト待機中に割り込まれた場合

それでは次はモジュール java.base の java.lang.Object java.lang.ref.Reference<T> java.lang.ref.PhantomReference<T> クラスを調べます。

クラスPhantomReference<T>

ファントム参照オブジェクトです。ファントム参照オブジェクトがキューに入れられるのは、キューに入れておかないとそれらのリファレントが再生される可能性があるとコレクタが判断したときです。

幻の参照は、死後のクリーンアップ・アクションをスケジュールするために最も頻繁に使用されます。

ある時点でオブジェクトが「ファントム到達可能」であるとガベージ・コレクタが判断したとします。

その時点で、そのオブジェクトへのすべてのファントム参照と、そのオブジェクトに到達可能な他のファントム到達可能オブジェクトへのすべてのファントム参照を原子的にクリアします。

同時に、または後で、参照キューに登録されたそれらの新たにクリアされたファントム参照をエンキューします。

再生可能なオブジェクトをそのままにしておくために、ファントム参照のリファレントを取り出すことはできません。ファントム参照のgetメソッドは、常にnullを返します。

コンストラクタは引数を二つ取るものが一つだけあります。

public PhantomReference​(T referent, ReferenceQueue<? super T> q)

指定されたオブジェクトを参照し、指定されたキューに登録されている新しいファントム参照を作成します。

nullキューでファントム・リファレンスを作成することは可能ですが、そのようなリファレンスはまったく役に立たない: そのgetメソッドは常にnullを返し、キューを持たないのでエンキューされません。

パラメータ:referent – 新しいファントム参照が参照するオブジェクト
q – 参照が登録されるキュー。登録が必要ない場合はnull

メソッドは一つだけあります。

public T get()

参照オブジェクトのリファレントを返します。

ファントム参照のリファレントは常にアクセス不可能なため、このメソッドは常にnullを返します。

戻り値:null

これで PhantomReference を使うための API をみてみました。

と、思いきや JDK 9 から java.​lang.​Object protected void finalize() throws Throwable が Deprecated になってしまいました。

代わりにモジュール java.base の java.lang.Object java.lang.ref.Cleaner クラスを使うことが推奨されているのでこちらもざっくりとみてみます。

クラスCleaner

Cleanerは、一連のオブジェクト参照と対応するクリーニング・アクションを管理します。

クリーナは、オブジェクトが有線で到達可能になったことがクリーナに通知された後で実行するために、クリーニング動作はregisteredです。

クリーナは、PhantomReferenceとReferenceQueueを使用して、reachabilityが変更されたときに通知を受け取ります。

各クリーナは、独立して動作し、保留中のクリーニング動作を管理し、クリーナがすでに使用されていないときにスレッドおよび終了を処理します。

オブジェクト参照と対応するクリーニング・アクションを登録すると、Cleanableが返されます。 最も効率的な使用法は、オブジェクトが閉じられたり不要になったときに、cleanメソッドを明示的に呼び出すことです。

クリーニング・アクションは、すでに明示的にクリーニングされていない限り、オブジェクトがファントム到達可能になったときに呼び出されるRunnableです。

クリーニング動作は、登録されているオブジェクトを参照してはならないことに注意してください。 そうであれば、オブジェクトは想像を絶するものにならず、クリーニング動作は自動的に呼び出されません。

クリーニング・アクションの実行は、クリーナに関連するスレッドによって実行されます。

クリーニング・アクションによってスローされたすべての例外は無視されます。

クリーナおよびその他のクリーニング・アクションは、クリーニング・アクションの例外の影響を受けません。

スレッドは、登録されているすべてのクリーニング処理が完了し、クリーナ自体がガベージ・コレクタによって再利用されるまで実行されます。

System.exit中のクリーナの動作は実装固有です。 クリーニング動作の呼び出しの有無は保証されていません。

ほかで指定がない場合、null引数をコンストラクタまたはこのクラスのメソッドへ渡すと、NullPointerExceptionがスローされます。

ネストされたクラスが一つあります。

public static interface Cleaner.Cleanable

Cleanableは、Cleanerに登録されているオブジェクトとクリーニング・アクションを表します。

ネストされたクラスにメソッドが一つだけあります。

void clean()

クリーニング可能なものの登録を解除し、クリーニング動作を呼び出します。 クリーニング可能なクリーニング動作は、cleanの呼び出し回数に関係なく、最大で1回呼び出されます。

Cleaner クラスにはメソッドが3個あります。

public static Cleaner create()

新しいCleanerを返します。

クリーナは、ファントム到達可能オブジェクトを処理し、クリーニング・アクションを呼び出すdaemon threadを作成します。

スレッドの「コンテキスト・クラス・ローダー」はsystem class loaderに設定されます。

スレッドはパーミッションを持たず、SecurityManager is setの場合にのみ実行されます。

ファントムに到達し、登録されたすべてのクリーニング動作が完了すると、クリーナは終了します。

戻り値:新しいCleaner

例外:SecurityException – 現在のスレッドがスレッドの作成または開始を許可されていない場合

public static Cleaner create​(ThreadFactory threadFactory)

ThreadFactoryからThreadを使用して新しいCleanerを返します。

スレッド・ファクトリのnewThreadメソッドからのスレッドはdaemon threadに設定され、ファントム到達可能オブジェクトを処理し、クリーン・アクションを起動します。

各呼び出しで、thread factoryは、クリーニング・アクションを実行するのに適したスレッドを提供する必要があります。

ファントムに到達し、登録されたすべてのクリーニング動作が完了すると、クリーナは終了します。

パラメータ:threadFactory – 新しいThreadを返してクリーニング・アクションを処理するThreadFactory

戻り値:新しいCleaner例外:IllegalThreadStateException – スレッド・ファクトリからのスレッドがnot a new threadの場合。
SecurityException – 現在のスレッドがスレッドの作成または開始を許可されていない場合。

public Cleaner.Cleanable register​(Object obj, Runnable action)

オブジェクトにファントム到達可能になったときに実行するオブジェクトとクリーニング・アクションを登録します。

クリーニング動作の動作については、上記の「APIノート」を参照してください。

パラメータ:obj – 監視するオブジェクト
action – オブジェクトがファントム到達可能になったときに呼び出すRunnable

戻り値:Cleanableインスタンス

これで今度こそ PhantomReference を使う為の必要最小限の情報をざっくりと API ドキュメントから学んだ。

簡単に PhantomReference ってけっきょくどうなの?ってのを考えてみると

ファントム到達可能オブジェクトを ReferenceQueue に入れる。

注意:) ファントム到達可能オブジェクトの ReferenceQueue への挿入はガーベジ・コレクトと非同期で行われます。よって GC が働いてすぐに挿入されると考えてはいけないようです。

Reference<T> クラスの三つあるメソッド poll()、remove()、remove​(long timeout) の何れかによって ReferenceQueue に入れられたオブジェクトを取り出してクリーンアップする。

これとは別に finalize() か Cleaner クラスを使ってクリーンアップする方法もあります。

クリーンアップの際はリソースの解放処理が必要であれば必ず実行するようにしておくのが無難かもしれない。

正しく理解できているかは怪しいけどプログラムを組んでみることにする。

とりあえず、前回、前々回に組んだ SoftReference と WeakReference の PhantomReference バージョンとしてみよう。

まず、JDK 1.8 対応のものから組んで、最新の JDK 11 で使える Cleaner クラスを使ったものに変更してみる。

そんなこんなで次のようなプログラムを組んでみました。

 

このプログラムは finalize() メソッドをオーバーライドして利用しています。

finalize() メソッド内で this.buff = null; として OutOfMemoryError を回避しています。(このようなことは JDK 11 では必要ではありませんでした。)

Reference<T> クラスの poll() メソッドはコメントアウトして実行できないようにしてあります。

ちなみに finalize() メソッドも使わなければ OutOfMemoryError でプログラムは途中で止まってしまいます。

プログラムの実行結果は次のようになります。

Windows 10 10.0 amd64
JDK 1.8.0_144

UsedMemory: 115595064   PS Scavenge   GC Count: 0   GCTime = 0     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 241927592   PS Scavenge   GC Count: 0   GCTime = 0     PS MarkSweep   GC Count: 0   GCTime = 0    
    — finalize —
    — finalize —
UsedMemory: 321118712   PS Scavenge   GC Count: 1   GCTime = 128     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 430987664   PS Scavenge   GC Count: 1   GCTime = 128     PS MarkSweep   GC Count: 0   GCTime = 0    
    — finalize —
    — finalize —
UsedMemory: 525473600   PS Scavenge   GC Count: 2   GCTime = 257     PS MarkSweep   GC Count: 0   GCTime = 0

UsedMemory: 6299540808   PS Scavenge   GC Count: 26   GCTime = 30760     PS MarkSweep   GC Count: 24   GCTime = 6659    
UsedMemory: 6404398424   PS Scavenge   GC Count: 26   GCTime = 30760     PS MarkSweep   GC Count: 24   GCTime = 6659    
UsedMemory: 6509256040   PS Scavenge   GC Count: 26   GCTime = 30760     PS MarkSweep   GC Count: 24   GCTime = 6659    
UsedMemory: 6614113656   PS Scavenge   GC Count: 26   GCTime = 30760     PS MarkSweep   GC Count: 24   GCTime = 6659

57秒ほどで終了しました。

ちょっと遅いですね。

NetBeans のプロファイラで確認してみます。

11

 

12

 

13

 

JDK 1.8 の標準のガーベジ・コレクタってこんなものなのか?

それでは先ほどのプログラムを Reference<T> クラスの poll() メソッドを使うように変更した実行結果は次のようになりました。

Windows 10 10.0 amd64
JDK 1.8.0_144

UsedMemory: 115595064   PS Scavenge   GC Count: 0   GCTime = 0     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 241927592   PS Scavenge   GC Count: 0   GCTime = 0     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 315733616   PS Scavenge   GC Count: 1   GCTime = 129     PS MarkSweep   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.PhantomReference@548c4f57 —
    — POLL:java.lang.ref.PhantomReference@1218025c —
UsedMemory: 425602568   PS Scavenge   GC Count: 1   GCTime = 129     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 525440640   PS Scavenge   GC Count: 2   GCTime = 260     PS MarkSweep   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.PhantomReference@816f27d —
    — POLL:java.lang.ref.PhantomReference@87aac27 —
UsedMemory: 640519896   PS Scavenge   GC Count: 2   GCTime = 260     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 745377512   PS Scavenge   GC Count: 2   GCTime = 260     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 850235128   PS Scavenge   GC Count: 2   GCTime = 260     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 955092744   PS Scavenge   GC Count: 2   GCTime = 260     PS MarkSweep   GC Count: 0   GCTime = 0    
UsedMemory: 629860128   PS Scavenge   GC Count: 3   GCTime = 569     PS MarkSweep   GC Count: 1   GCTime = 43    
    — POLL:java.lang.ref.PhantomReference@3e3abc88 —

    — POLL:java.lang.ref.PhantomReference@cb644e —
UsedMemory: 5880074344   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 5984931960   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6089789576   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6194647192   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6299504808   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6404362424   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6509220040   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6614077656   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6718935272   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566    
UsedMemory: 6823792888   PS Scavenge   GC Count: 26   GCTime = 30258     PS MarkSweep   GC Count: 24   GCTime = 6566

56秒で終了しました。

finalize() メソッドを使ったのとほぼ同じですね。

ちなみに64行目の ref.clear() メソッドを実行して参照オブジェクトをクリアしないと OutOfMemoryError でプログラムは途中で止まってしまいます。

JDK 11 では clear() メソッドで参照オブジェクトをクリアしなくてもOutOfMemoryError でプログラムで止まることはありません。

いちおう NetBeans のプロファイラで確認します。

14

 

15

 

16

 

大きな違いはなさそうです。

それでは JDK 11 に切り替えて finalize() メソッド を利用したプログラムを実行してみます。

JDK 1.8 との比較のためにあえて finalize() メソッド を使ってます。あとで Cleaner クラスを使ったものも試してみます。

プログラムの実行結果は次のようになりました。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
    — finalize —
    — finalize —
    — finalize —
    — finalize —
UsedMemory: 546494784   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 655546688   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
    — finalize —
    — finalize —
UsedMemory: 328435288   G1 Young Generation   GC Count: 2   GCTime = 7     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 437487192   G1 Young Generation   GC Count: 2   GCTime = 7     G1 Old Generation   GC Count: 0   GCTime = 0    

    — finalize —
    — finalize —
    — finalize —
UsedMemory: 2073652944   G1 Young Generation   GC Count: 106   GCTime = 244     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 2182704848   G1 Young Generation   GC Count: 106   GCTime = 244     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 2291756752   G1 Young Generation   GC Count: 106   GCTime = 244     G1 Old Generation   GC Count: 0   GCTime = 0

15秒で終了しました。

JDK 11 のガーベジ・コレクタ優秀ですね。

確か JDK 9 から G1GC が標準になったはず。

これも NetBeans のプロファイラで覗いてみます。

17

 

18

 

19

 

この違いを目の当たりにすると JDK 1.8 はもう使えない。(^_^;)

では、Reference<T> クラスの poll() メソッドを使うように変更して実行してみます。

プログラムの実行結果は次のようになります。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 110281288   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.PhantomReference@d8355a8 —
    — POLL:java.lang.ref.PhantomReference@5e5792a0 —
    — POLL:java.lang.ref.PhantomReference@26653222 —
    — POLL:java.lang.ref.PhantomReference@3532ec19 —
UsedMemory: 219333192   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 328385096   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 437437000   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 110345216   G1 Young Generation   GC Count: 2   GCTime = 7     G1 Old Generation   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.PhantomReference@68c4039c —

    — POLL:java.lang.ref.PhantomReference@5db250b4 —
    — POLL:java.lang.ref.PhantomReference@223f3642 —
UsedMemory: 219733600   G1 Young Generation   GC Count: 99   GCTime = 208     G1 Old Generation   GC Count: 0   GCTime = 0

これも15秒で終了しました。

NetBeans のプロファイラで覗いてみます。

20

 

21

 

22

 

こちらのほうが僅かだけど良いかもしれない。

JDK 1.8 と JDK 11 の違いはガーベジ・コレクタが変更されたくらいだと思うのですけど他にも何か変更があるのかもしれません。

ちょっと気になるので JDK 1.8 と JDK 11 のスレッドを確認してみます。

両方とも Reference<T> クラスの poll() メソッドを使用した場合です。

まず、JDK 1.8 から

23

次は JDK 11です。

24

JDK 11 のほうに Common-Cleaner という見慣れないスレッドがありますね。

これがパフォーマンスアップの要因の一つなのかな。

GC 関連のことはあまり気にしたことないのでよく解りません。(>_<。)

名前からして勝手にクリーンアップしてくれそうなので finalize() メソッド、Reference<T> クラスの poll() メソッド両方ともコメントアウトして無効にして実行してみました。

OutOfMemoryError でプログラムが止まりまし・・・  あれっ! 止まらない。

プログラムは15秒で終了しました。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 110281920   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 219333824   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 328385728   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 437437632   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 110325528   G1 Young Generation   GC Count: 2   GCTime = 6     G1 Old Generation   GC Count: 0   GCTime = 0    

UsedMemory: 1092084152   G1 Young Generation   GC Count: 98   GCTime = 200     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1201136056   G1 Young Generation   GC Count: 98   GCTime = 200     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 110617168   G1 Young Generation   GC Count: 99   GCTime = 202     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 219669072   G1 Young Generation   GC Count: 99   GCTime = 202     G1 Old Generation   GC Count: 0   GCTime = 0    
BUILD SUCCESSFUL (total time: 15 seconds)

いったいどういうことなんだ?

クリーンアップの際にリソースの解放処理が必要で無いのなら何もしなくてもいいのか?

JDK 1.8 で Reference<T> クラスの poll() メソッドを使う場合は clear() メソッドで参照オブジェクトをクリアして次の GC で解放されるという最低でも GC が二回必要だったのに・・・

ガーベジ・コレクタ凄く賢くなっている。(私が PhantomReference の使い方を間違ってるかもしれない)

このあたりのことを調べ出すとガーベジ・コレクタ沼にはまって苦しむことになるので誰か親切な人が詳しく解りやすい解説記事をネット上にアップしてくれるのを待つことにする。

それでは最後に Cleaner クラス を使ってみます。

この Cleaner クラスもファイナライザ同様にあまり使うのを好ましく思われていません。

詳細は「Effective Java 第3版」の第2章 オブジェクトの生成と消滅 項目 8 ファイナライザとクリーナーを避ける をご覧ください。

 

それでは Cleaner クラスを使ってみます。

これまでのプログラムを JDK 10 以降の新しいものを使うことを前提にして組み直しました。

 

30行目で Cleaner を生成、31行目でファントム到達可能になったときに実行するオブジェクトとクリーニング・アクションを登録します。

var cleaner = Cleaner.create();
cleaner.register(memoryConsumer, new CleanerRunnable(memoryConsumer.buff, i));

クリーニング・アクションの登録でnew CleanerRunnable(memoryConsumer.buff, i) と無駄なことをしています。

これはお遊びですので引数無しでクリーニング・アクションの run() メソッド内のゴニョゴニョしたコードは無くて空でも問題無いです。

84行目の buff = null; もあっても無くてもほぼ変わりはありません。(少しは変わるかもと思って試しただけです)

たったこれだけです!非常に便利です。

これでファイナライザ攻撃の心配はなくなります。

プログラムの実行結果は次のようになります。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 554883024   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 663934928   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 772986832   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 882038736   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 991090640   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1096034664   G1 Young Generation   GC Count: 2   GCTime = 5     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1205086568   G1 Young Generation   GC Count: 2   GCTime = 5     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1314138472   G1 Young Generation   GC Count: 2   GCTime = 5     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1419045944   G1 Young Generation   GC Count: 3   GCTime = 8     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1532292152   G1 Young Generation   GC Count: 3   GCTime = 8     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 6
Cleaning: 1
Cleaning: 5
Cleaning: 3
Cleaning: 7
Cleaning: 0
Cleaning: 2
Cleaning: 4
Cleaning: 9
UsedMemory: 1645538360   G1 Young Generation   GC Count: 3   GCTime = 8     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 11
Cleaning: 10
Cleaning: 8
UsedMemory: 1754590264   G1 Young Generation   GC Count: 3   GCTime = 8     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1863642168   G1 Young Generation   GC Count: 3   GCTime = 8     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 16
Cleaning: 15
Cleaning: 13
Cleaning: 14
Cleaning: 12
UsedMemory: 655688552   G1 Young Generation   GC Count: 4   GCTime = 12     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 764740456   G1 Young Generation   GC Count: 4   GCTime = 12     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 873792360   G1 Young Generation   GC Count: 4   GCTime = 12     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 19
Cleaning: 17
Cleaning: 18
UsedMemory: 437556936   G1 Young Generation   GC Count: 5   GCTime = 15     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 546608840   G1 Young Generation   GC Count: 5   GCTime = 15     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 655660744   G1 Young Generation   GC Count: 5   GCTime = 15     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 764712648   G1 Young Generation   GC Count: 5   GCTime = 15     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 873764552   G1 Young Generation   GC Count: 5   GCTime = 15     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 20
Cleaning: 21
Cleaning: 24

UsedMemory: 1314754576   G1 Young Generation   GC Count: 170   GCTime = 285     G1 Old Generation   GC Count: 0   GCTime = 0    
Cleaning: 993
Cleaning: 992
Cleaning: 997
Cleaning: 995
Cleaning: 996
Cleaning: 991
Cleaning: 994
UsedMemory: 882748320   G1 Young Generation   GC Count: 171   GCTime = 287     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 991800224   G1 Young Generation   GC Count: 171   GCTime = 287     G1 Old Generation   GC Count: 0   GCTime = 0

16秒で終了しました。

Cleaner クラス使うと便利ですね。

ファントム到達可能になったときに実行するオブジェクトとクリーニング・アクションを登録するだけだし、それにクリーニング・アクション用の専用スレッドが起動される。

ファイナライザを使って何かしようとすると自前でスレッドを用意したりしなくちゃいけない場合がある。

ファイナライザ攻撃によるセキュリティ上の問題も無い。

もう、ファイナライザを使う必要は無いような気がする。

どのみち近い将来使えなくなるしね。

パッケージ java.lang.ref を良く解らないままいろいろやってみたけど PhantomReference って扱いが難しいですね。

そもそもファントム到達オブジェクトって強参照、ソフト参照、弱参照のどれから辿っても到達できなくなってファイナライザ呼び出しが完了した状態ってところで使いどころが限定されそうな気がする。

ReferenceQueue に入れられた PhantomReference の get() メソッドは null を常時返すようになっている。

つまり、これって GC 対象ってことですよね。

だめだ、頭痛が痛くなりそうだ。(まさに今こんな感じです。)

PhantomReference ってただのクリーンアップ用にしか思えなくなってきた。(^_^;)

しかも、JDK 1.8 の頃のほうが不思議な動作をせず理解しやすいけど JDK 11 との違いは大きすぎる。

パフォーマンスアップや使い勝手が良くなるのはいいけど解りやすい情報が乏しいのはなんとかしてほしい。

今日でお正月休みも終わってしまうけどもっと Java を楽しめる一年になるようにがんばるぞい!

Hatena タグ: ,

WeakReference

Java NetBeans

このエントリーは、SoftReference エントリーの続きです。

http://yucchi.jp/blog/?p=2675

SoftReference で組んだプログラムの実行結果が想像していたのと違っていたので今度は WeakReference で遊んでみます。

まず、WeakReference ってどんなものなのか調べてみます。

APIドキュメントには次のように記されてます。

弱参照オブジェクトです。弱参照オブジェクトは、その弱参照オブジェクトのリファレントがファイナライズ可能になり、ファイナライズされ、そして再生されることを阻止することはありません。

弱参照は、ほとんどの場合で正規化マッピングを実装するために使用されます。

ある時点で、オブジェクトが弱到達可能であると、ガベージ・コレクタが判断したとします。

その時点で、ガベージ・コレクタは、そのオブジェクトへの弱参照すべてと、強参照およびソフト参照のチェーンを経由してそのオブジェクトに到達できるような、ほかの弱到達可能なオブジェクトへの弱参照すべてを、原子的にクリアします。

同時に、ガベージ・コレクタは以前に弱到達可能なオブジェクトがすべてファイナライズ可能であることを宣言します。

同時にまたはあとで、ガベージ・コレクタは、参照キューに登録されているそれらの新しくクリアされた弱参照をキューに入れます。

WeakReference では弱到達可能になったら問答無用で何も考えずに GC が働くときに無条件で参照を解放されるってことかな。

SoftReference のように参照の使用頻度やヒープの状況を考慮して参照を解放するのとは大違いですね。

ちなみにこの WeakReference はこの特性をいかして WeakHashMap に使われていますね。

java.util.WeakHashMap: キーが弱可到達になったらエントリごと削除する Map

WeakReference クラスにはコンストラクタが二つあります。

WeakReference​(T referent)

指定されたオブジェクトを参照する、新しい弱参照を作成します。新しい参照は、どのキューにも登録されません。

WeakReference​(T referent, ReferenceQueue<? super T> q)

指定されたオブジェクトを参照し、指定されたキューに登録されている新しい弱参照を作成します。

パラメータ:referent – 新しい弱参照が参照するオブジェクトq – 参照が登録されるキュー。登録が必要ない場合はnull

メソッドは無いですね。

それではプログラムを組んで試してみます。

前回組んだプログラムと同じように無駄にメモリーを消費するインスタンスを1000個作っているだけです。

今回のプログラムには無駄にメモリーを消費するオブジェクトに finalize() メソッドを実装しました。

特にそれによって何かするわけでなく呼ばれたことを確認するためだけです。

 

 

プログラムの実行結果は次のようになりました。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
    — finalize() —
    — finalize() —
    — finalize() —
    — finalize() —
UsedMemory: 546492432   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.WeakReference@d8355a8 —
    — POLL:java.lang.ref.WeakReference@59fa1d9b —
    — POLL:java.lang.ref.WeakReference@28d25987 —
    — POLL:java.lang.ref.WeakReference@4501b7af —
    — finalize() —
UsedMemory: 219385672   G1 Young Generation   GC Count: 2   GCTime = 6     G1 Old Generation   GC Count: 0   GCTime = 0    
    — POLL:java.lang.ref.WeakReference@523884b2 —
UsedMemory: 332631880   G1 Young Generation   GC Count: 2   GCTime = 6     G1 Old Generation   GC Count: 0   GCTime = 0    

UsedMemory: 1201289840   G1 Young Generation   GC Count: 115   GCTime = 252     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1310341744   G1 Young Generation   GC Count: 115   GCTime = 252     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 1419393648   G1 Young Generation   GC Count: 115   GCTime = 252     G1 Old Generation   GC Count: 0   GCTime = 0

SoftReference とは大違いです。

プログラムの処理時間も15秒と非常に短い時間です。

GC 累積時間もぜんぜん違います。

NetBeans のプロファイラで確認してみます。

1

 

2

 

3

 

SoftReference と比べると使用しているヒープメモリーサイズも全然少ないし、GC タイムも雲泥の差がある。

これを見るとなるだけ SoftReference は使いたくないと思ってしまう。

でも、SoftReference だったらソフト到達可能なオブジェクトへのすべてのソフト参照は、仮想マシンが OutOfMemoryError をスローする前にクリアされていることが保証されている。

SoftReference はメモリの容量に応じてスケールするキャッシュ用って感じかな。

WeakReference は弱参照オブジェクトが弱到達可能になったらガベージ・コレクタでクリアしたい時に使う(キャッシュしない)。

API ドキュメントの文章が難しく書かれているのであまり自信はないけどこのような違いがあるようだ。

プログラムの実行結果からもその様子が少しは解ります。

ちなみに SoftReference と WeakReference の違いはプログラムの出力結果のガベージ・コレクタの動作ではっきりと解ります。

WeakReference の弱到達可能なオブジェクトは Young Generation 領域に対するマイナー GC ですぐ回収されています。

それに対して SoftReference のソフト到達可能オブジェクトは G1 Old Generation 領域に移されヒープが不足して OutOfMemoryError をスローしないところまで回収されないようです。

SoftReference: UsedMemory: 6871416088   G1 Young Generation   GC Count: 222   GCTime = 332     G1 Old Generation   GC Count: 12   GCTime = 8964

WeakReference: UsedMemory: 1419393648   G1 Young Generation   GC Count: 115   GCTime = 252     G1 Old Generation   GC Count: 0   GCTime = 0

なかなか面白い仕組みになっていますね。

強参照しか普段使わない私にとって新鮮な気分です。

しかし、これって JDK 1.2 から追加された古い API のようです。(^_^;)

いったいどれだけのプログラマが意識して使っているのか、いや・・・使わざるを得なかったのか・・・

さて、次は PhantomReference を試してみよう。

たぶん、続く。

Hatena タグ: ,

SoftReference

Java NetBeans

あけましておめでとうございます。

とうとう今年の5月で平成が終わってしまいます。

新しい元号がまだ発表されてないので一部の人は困っているようですがどうにかならないものなんでしょうか?

さて、どうにもならないことをどうかしようとすると頭が痛くなるから現実逃避のために Java で遊ぶとしよう。

SoftReference って聞いたことはあるけど実際にプログラムを組む上で使った記憶がないのでこれで遊んでみよう。

java.base モジュールの java.lang.ref パッケージにある public class SoftReference<T> extends Reference<T> クラスです。

API ドキュメントには下記のように記されてます。

メモリー要求に応じてガベージ・コレクタの判断でクリアされるソフト参照オブジェクトです。

ソフト参照は通常、メモリー・センシティブなキャッシュを実装するために使用されます。

ある時点で、オブジェクトがソフト到達可能であると、ガベージ・コレクタが判断したとします。

その時点で、ガベージ・コレクタは、そのオブジェクトへのソフト参照すべてと、強参照のチェーンを経由してそのオブジェクトに到達できるような、ソフト到達可能なほかのオブジェクトへのソフト参照すべてを原子的にクリアすることを選択できます。

同時にまたはあとで、ガベージ・コレクタは、参照キューに登録されているそれらの新しくクリアされたソフト参照をキューに入れます。

ソフト到達可能なオブジェクトへのすべてのソフト参照は、仮想マシンがOutOfMemoryErrorをスローする前にクリアされていることが保証されています。

そうでない場合、ソフト参照がクリアされる時点、またはさまざまなオブジェクトへの一連のソフト参照がクリアされる順序に制約はありません。

ただし、仮想マシンの実装は、最近作成されたソフト参照または最近使用されたソフト参照をクリアしないことが奨励されます。

このクラスの直接のインスタンスは、単純なキャッシュを実装するために使用できます。

このクラスまたは派生したサブクラスは、より洗練されたキャッシュを実装するために、もっと大きなデータ構造でも使用できます。

ソフト参照のリファレントが強到達可能であるかぎり、つまり実際に使用されているかぎり、ソフト参照はクリアされません。

このため、たとえば洗練されたキャッシュは、エントリへの強いリファレントを維持することで、もっとも新しく使用されたエントリが破棄されることを防ぎ、ほかのエントリはガベージ・コレクタの判断で破棄されるようにできます。

つまり、ガベージ・コレクタが参照の使用頻度とヒープの状態をみて参照の解放を勝手にやってくれるので OutOfMemoryError でプログラムが止まってしまうことがない。

ガベージ・コレクタが必死でがんばってくれる!私は感謝するぞ!

で、この SoftReference<T> クラスにはコンストラクタが2種類あります。

SoftReference​(T referent)

指定されたオブジェクトを参照する新しいソフト参照を作成します。新しい参照は、どのキューにも登録されません。

パラメータ:referent – 新しいソフト参照が参照するオブジェクト

SoftReference​(T referent, ReferenceQueue<? super T> q)

指定されたオブジェクトを参照し、指定されたキューに登録されている新しいソフト参照を作成します。

パラメータ:referent – 新しいソフト参照が参照するオブジェクトq – 参照が登録されるキュー。登録が必要ない場合はnull

メソッドは一つだけあります。

public T get()

参照オブジェクトのリファレントを返します。 プログラムまたはガベージ・コレクタによって、この参照オブジェクトがすでにクリアされている場合、このメソッドはnullを返します。

戻り値:この参照が表すオブジェクト。この参照オブジェクトがクリアされている場合はnull

もの凄く質素なクラスですね。

そう言うことでさっそくお試しプログラムを組んでみました。

 

 

無駄にメモリーを消費するインスタンスを1000個作っているだけです。

普通なら OutOfMemoryError でプログラムが止まってしまうはずですが SoftReference を使って賢いガベージ・コレクタの力で完走させようというものです。

プログラムの実行結果は次のようになりました。

Windows 10 10.0 amd64
JDK 11.0.1

UsedMemory: 113246208   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 222298112   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 331350016   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 440401920   G1 Young Generation   GC Count: 0   GCTime = 0     G1 Old Generation   GC Count: 0   GCTime = 0    
UsedMemory: 546493104   G1 Young Generation   GC Count: 1   GCTime = 3     G1 Old Generation   GC Count: 0   GCTime = 0

UsedMemory: 110148312   G1 Young Generation   GC Count: 57   GCTime = 109     G1 Old Generation   GC Count: 2   GCTime = 1808    
    — POLL:java.lang.ref.SoftReference@59fa1d9b —
    — POLL:java.lang.ref.SoftReference@28d25987 —

    — POLL:java.lang.ref.SoftReference@527740a2 —
    — POLL:java.lang.ref.SoftReference@13a5fe33 —
UsedMemory: 219243560   G1 Young Generation   GC Count: 58   GCTime = 110     G1 Old Generation   GC Count: 2   GCTime = 1808    
UsedMemory: 328295464   G1 Young Generation   GC Count: 58   GCTime = 110     G1 Old Generation   GC Count: 2   GCTime = 1808 

UsedMemory: 6762364184   G1 Young Generation   GC Count: 222   GCTime = 332     G1 Old Generation   GC Count: 12   GCTime = 8964    
UsedMemory: 6871416088   G1 Young Generation   GC Count: 222   GCTime = 332     G1 Old Generation   GC Count: 12   GCTime = 8964

プログラムが終了するまで1分8秒もかかった。

私の7年前のパソコンが壊れたのかと思ってしまった。(^_^;)

NetBeans のプロファイラでどんなかんじなのか確認してみました。

1

 

2

 

3

 

確かに OutOfMemoryError でプログラムが止まらないようにガベージ・コレクタが頑張っているようだけどGC時間長くない?

プログラムの実行結果からも気のせいか GC の回数、累積時間が長いような気がする。

SoftReference ってこんなものなのかなぁ・・・

あっ、なにかプログラムに問題があるのかもしれない。

これは WeakReference で試して比較してみるしかない。

ということで次回に続く

Hatena タグ: ,

Jigsaw のお勉強 その4 JavaFX (Spinner がほんの少しだけ良くなった)

Java JavaFX

このエントリーは じゃばえふえっくす Advent Calendar 2018 の初日(12月4日)です。

未だにJigsawのお勉強をしています。(^_^;)

今回はJavaFXを使ってみます。

そんなJavaFXですが正式リリース前からさらにリリースされてからもいろいろありました。

現在はJavaにバンドルされていません。

正確にはいろいろなところからJDKが提供されているのでひょっとしたらJavaFXをバンドルしたJavaがあるのかもしれません。

今回はOpenJDKとOpenJFXを使います。

JavaFX 11でjavafx.scene.control.Spinner<T>クラスにほんの少し優しい変更が追加されたのでそれを試してみました。

矢印ボタンをマウスにて押し続けたときの入力時間がJavaFX 8の750ms固定だったのが自由に変更できるようになりました。

InitialDelayとRepeatDelayが設定可能となります。

デフォルトの設定時間はそれぞれ300ms、60ms となっています。

まず、Spinnerについて少しだけ復習しておきましょう。

SpinnerのAPIドキュメントに下記のように書かれています。

順序付けられたシーケンスからユーザーが数値またはオブジェクト値を選択できるようにする単一行のテキスト・フィールド。

通常、スピナーはシーケンスの要素間を移動するための小さな矢印ボタンのペアを提供します。

キーボードの上/下矢印キーでも要素間を自由に移動できます。

ユーザーがスピナーに直接(有効な)値を入力することもできます。

コンボ・ボックスも同様の機能を提供しますが、スピナーの方が好まれる場合があるのは、重要なデータを不明瞭化する可能性があるドロップ・ダウン・リストが不要であり、また、他の多くのJavaFX UIコントロールのようにObservableListデータ・モデルを使用せずに、wrappingなどの機能や、より単純な’無限’データ・モデルの仕様(SpinnerValueFactory)を使用できるためです。

Spinnerのシーケンス値はSpinnerValueFactoryで定義します。

value factoryはコンストラクタ引数として指定し、value factory propertyを使用して変更できます。

JavaFXには、次に示す一般的なタイプのSpinnerValueFactoryクラスが用意されています。

•SpinnerValueFactory.IntegerSpinnerValueFactory

•SpinnerValueFactory.DoubleSpinnerValueFactory

•SpinnerValueFactory.ListSpinnerValueFactory

Spinnerには、Spinnerの現在のvalueの表示および変更を行う、editorと呼ばれるTextField子コンポーネントがあります。

Spinnerはデフォルトで編集不可能ですが、editable propertyをtrueに設定すると、入力を受け入れることができます。

Spinnerエディタは、値ファクトリのvalue propertyに対する変更をリスニングすることにより、値ファクトリとの同期を保ちます。 ユーザーがeditorに表示された値を変更した場合、Spinnerのvalueとeditorの値が異なってしまう可能性があります。

モデルの値をeditorの値と同じにするには、ユーザーが[Enter]キーを使用して編集をコミットする必要があります。

APIドキュメントによると一般的なタイプのSpinnerValueFactoryクラスと使ってSpinnerインスタンスを作成できると書かれていました。

Spinner​(double min, double max, double initialValue) // SpinnerValueFactory.DoubleSpinnerValueFactory

Spinner​(double min, double max, double initialValue, double amountToStepBy) // SpinnerValueFactory.DoubleSpinnerValueFactory

Spinner​(int min, int max, int initialValue) // SpinnerValueFactory.IntegerSpinnerValueFactory

Spinner​(int min, int max, int initialValue, int amountToStepBy) // SpinnerValueFactory.IntegerSpinnerValueFactory

Spinner​(ObservableList<T> items) // SpinnerValueFactory.ListSpinnerValueFactory

Spinner​(SpinnerValueFactory<T> valueFactory) // 指定されたvalue factoryを設定する場合

Spinnerインスタンスを作成するのは容易にできることが確認できたので次はSpinnerの定数フィールド値を使ってスタイルの設定方法を調べてみます。

public static final String STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL

水平方向(左向きと右向き)の矢印がSpinnerの左側に配置されます。

public static final String STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL

垂直方向(上向きと下向き)の矢印がSpinnerの左側に配置されます。

public static final String STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL

水平方向(左向きと右向き)の矢印がSpinnerの右側に配置されます。

public static final String STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL

Spinnerの左側に減分矢印、右側に増分矢印が配置されます。

public static final String STYLE_CLASS_SPLIT_ARROWS_VERTICAL

スピナーの幅全体にわたって上下に伸びた矢印が配置されます。

何も指定をしない場合はデフォルトの垂直方向(上向きと下向き)の矢印がSpinnerの右側に配置されます。

Spinnerインスタンスの作成、スタイルの設定方法が解ったので試してみます。

Spinnerのデザインをデフォルトの垂直方向(上向きと下向き)の矢印がSpinnerの右側に配置

1

Spinnerのデザインを水平方向(左向きと右向き)の矢印がSpinnerの左側に配置
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_HORIZONTAL);

2

Spinner のデザインを垂直方向(上向きと下向き)の矢印がSpinnerの左側に配置
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_LEFT_VERTICAL);

3

Spinnerのデザインを水平方向(左向きと右向き)の矢印がSpinnerの右側に配置
spinner.getStyleClass().add(Spinner.STYLE_CLASS_ARROWS_ON_RIGHT_HORIZONTAL);

4

Spinnerのデザインを左側に減分矢印、右側に増分矢印が配置
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);

5

Spinnerのデザインをスピナーの幅全体にわたって上下に伸びた矢印が配置
spinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_VERTICAL);

6

// 最小値 0、最大値 100、初期値 50、増分または減分するステップの量のSpinnerインスタンス作成
Spinner<Integer> spinner = new Spinner<>(0, 100, 50, 10);

javafx.​scene.​Nodeパッケージのpublic final ObservableList<String> getStyleClass()でノードを論理的にグループ化するために使用できる文字列識別子のリストを取得しava.​util.​Listパッケージのpublic abstract boolean add(E arg0)メソッドでSpinnerの定数フィールド値を追加して設定します。

Spinnerの増分、減分の矢印ボタンの配置デザインの設定はこれで可能となりました。

次に Spinnerに表示されている値のアライメントとカスタムフォントを設定してみます。

//  カスタムフォント、アライメントを設定
Font f = Font.loadFont(this.getClass().getResourceAsStream(“resources/AKUBIN1.34.ttf”), 28);
spinner.getEditor().setStyle(“-fx-text-fill: black; -fx-font: 18px ‘Akubin’; -fx-alignment: CENTER_RIGHT;”);

SpinnerExample

上の行でカスタムフォントを読み込んでその下の行でSpinnerにそのフォントを設定、テキストのサイズ、文字色、アライメントを設定しています。

そしてついでに

// Spinneのエディタをユーザー入力可能とする
spinner.setEditable(true);

としてユーザー入力可能としてます。

増分、減分のステップ量は変わらずユーザーによって入力された値からの増分値、減分値となります。

また、最大値、最小値を超えることはありません。

ここまでのプログラムのソースコードは次のようになります。

それではSpinnerにInitialDelayとRepeatDelayを設定するプログラムを組んでみます。

せっかくだからJDK 12 Early-Access Builds (build: 21)とJavaFX Early-Access Builds (openjfx-12-ea+2)を使ってみます。

さらに、モジュラjarを作ってモジュール化します。

そして、jlinkで配布用にカスタムJREをバンドルしたアプリケーションを作成します。

NetBeans IDE 10.0-vc4を使ってお気楽に組もうとしたがこのバージョンには未対応なので古典的なツールを使いました。

ちなみにNetBeans IDE 10.0 Devでプロジェクトをビルドし、jlinkでカスタムJRE組み込みアプリケーションの作成はできました。

ただ、エラーバッジの嵐のエディタは心が痛みます。

それではお気に入りのエディタか心を痛めながらNetBeans IDE 10.0-vc4を使って下記ファイルを作成します。

 

 

 

 

プロジェクトの構成は下図のようにします。

11

カスタムフォントを使うのでお気に入りのフォントをご用意ください。

コンパイル時にはJDK 12の新機能を使えるように–release 12 –enable-preview VMオプションをつけます。

Java SE 11からJavaFXはバンドルされなくなったのでそれらライブラリをダウンロードしてコンパイル、実行時にモジュールパスを指定します。

JavaFXはこちらから入手できます。

https://gluonhq.com/products/javafx/

JavaFX Linux SDKとJavaFX Linux jmodsがあります。

JavaFX Linux SDKだけでいいのですがjlinkで配布用にカスタムJREをバンドルしたアプリケーションを作成する場合はJavaFX Linux jmodsが必要となります。

-d オプションでbin/IncludeeJavaFXディレクトリにコンパイルによって作成されるクラスファイルが格納されるようにしています。

–module-pathオプションでJavaFX SDKのパスを指定します。

–add-modulesオプションでアプリケーションで必要とされるモジュールを指定します。

12

FXMLDocument.fxmlはコンパイルを実行しても出力ディレクトリbin/IncludeeJavaFXには移動、コピーされないので存在しません。

FXMLDocument.fxmlをコンパイルして作成されたクラスファイルのある場所にコピーします。

13

フォントを格納しているresourcesディレクトリもコピーします。

14

プロジェクトの構成は下図のようになります。

15

それでは実行してみましょう。

–enable-preview VMオプションを忘れずに。

–module-path、–add-modules、–moduleオプションも必要です。

16

ちゃんとコンパイルされて実行できました。

17

と、思いきや

18

何やら妙な警告がでました。(>_<。)

GTK libraries version 3を使うとだめそうなので-Djdk.gtk.verbose=true -Djdk.gtk.version=2とVM オプションをあたえます。

19

これってJavaFX 12のリリースまでには修正されるのかな?

20

それではこのプログラムをモジュラjar化してみます。

modsディレクトリを作成してそこにモジュラjarを格納します。

–module-versionでこのモジュールのバージョンを設定します。

実行時メインクラスの指定をしなくていいように–main-classオプションを使ってメインクラスを指定してアプリケーションエントリポイントを設定します。

21

モジュラjarがちゃんとできたか確認します。

22

問題無さそうなのでプログラムを走らせてみます。

–module-pathにはこのアプリケーションを格納しているmodsディレクトリとJavaFX SDKのパスを指定します。

–moduleオプションはメインクラスの指定を省力できるようにしたのでモジュール名だけで実行できます。

モジュラjar作成時にメインクラスの指定をしなかった場合は–module モジュール名/モジュールのメインクラス名とアプリケーションエントリポイント指定しなければいけません。

今回の場合だと、–module IncludedJavaFX/jp.yucchi.includedjavafx.IncludedJavaFXとします。

23

アプリケーションのモジュール化に成功し、ちゃんと動くことが確認できました。

24

最後にjlinkで配布用にカスタムJREをバンドルしたアプリケーションを作成します。

includedjavafx-imageディレクトリを作成しそこに成果物を格納します。

–module-pathオプションでJDKとJavaFXのモジュールを指定します。JavaFX Linux SDKのパスじゃなくてJavaFX Linux jmodsのパスを指定することに注意してください。

–add-modulesオプションでこのアプリケーション自体を指定します。

–launcherオプションを次のように設定します。

–launcher <コマンド名>=<モジュール名><メインクラス>

–outputオプションで出力先を設定します。

25

jlinkによって作成されたランチャーファイルにVMオプションを記述します。

26

27

これを忘れると少し焦ります。(^_^;)

28

動きましたね。

JavaがインストールされてないPCでも動くことを確認しました。(Linux)

ただしWindowsでは動きません。

Windowsで動く配布用アプリはWindowsで作成しなくてはいけないようです。

Write once, run anywhere ってのは遠い過去の夢になってしまったのでしょうか・・・

Spinner

肝心のことを忘れていました。

上のGifアニメで増分、減分ボタンを押しっぱなしにしている時のSpinnerの更新時間に注目してください。

// タイマーの時間を設定
versionSpinner.setInitialDelay(Duration.millis(INTIAL_DELAY_TIME));
versionSpinner.setRepeatDelay(Duration.millis(REPEAT_DELAY_TIME));

setInitialDelay​(Duration value) メソッドで押し続けて最初に更新される時間を設定しています。

このプログラムでは解りやすくするために1500ミリ秒に設定しました。

そのまま押し続けた時の更新時間(リピート時間)は

setRepeatDelay​(Duration value) メソッドで 500ミリ秒と設定しました。

Java SE 8 u40 では750ミリ秒に固定されていて変更することができなかったので少しうれしいアップデートです。

Swingも悪くはないけど近い将来にはJavaの標準 GUI はJavaFXになってほしい。

 

Jigsawにもほんの少しだけ慣れてきたけど難しいですね。

モジュールの依存関係を調べるのがお手軽にできるツールがあればいいのだけど・・・

jdepsは万能ではないので落とし穴に落ちます。(>_<。)

今回のプログラムではFXMLを利用しています。

JavaFXでGUIの構築にはとても便利です。

で、それを使うためにmodule-info.javaにrequires javafx.fxml;とrequiresモジュールディレクティブ設定します。

ところがそれだけではjava.lang.reflect.InvocationTargetExceptionが投げられます。

FXMLを利用したJavaFXアプリケーションではjavafx.fxmlモジュール側からこのプログラムのFXMLのコントローラークラスに対してリフレクションによるアクセスがあります。

モジュールのカプセル化はリフレクションにも適用されるからです。

JDK 9より前はリフレクションを使えばパッケージ内の全てのクラスやクラスの全てのメンバーにアクセスできました。

つまりリフレクションは開発者の本意と関係なくカプセル化を破壊することが可能です。

Jigsawでは強力で繊細なカプセル化が提供されています。

デフォルトでは外部モジュールからモジュール内のクラスにアクセスすることはできません。

外部モジュールからアクセスできるのはexportsされたパッケージの公開クラス、公開メンバだけなのです。

非公開なメンバへのアクセスは外部モジュールから禁止されています。

こういった所でもモジュール化のメリットである依存性、公開範囲の制限が絡んでくるんですね。

なのでこのリフレクションによるjavafx.fxmlモジュール側からのアクセスを許可する必要があります。

それにはopens…toモジュール・ディレクティブを設定します。

module-info.javaにopens jp.yucchi.includedjavafx to javafx.fxml;とopensモジュールディレクティブを付け加えるだけです。

module-info.javaにこのモジュールディレクティブを加えることによって実行時にのみ特定のモジュールに対してパッケージへのアクセスを許可することが可能です。

つまり、特定のパッケージ内のpublic型(およびネストされているpublic型とprotected型)に対して実行時にのみアクセスできることを指定可能となります。

指定したパッケージのすべての型(およびその型のすべてのメンバ)にリフレクションを使ってアクセスすることもできます。

Jigsawの強力で繊細なカプセル化はJDK 9より前のものより開発者には喜ばれるはずですね。

そうなるとjdepsのような依存性を解決する強力なツールの必要性が求められます。

そのうち統合開発環境なんかに乗っかってくることを期待します。

 

Jigsaw のお勉強 その1

Jigsaw のお勉強 その2

Jigsaw のお勉強 その3

Hatena タグ: ,


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Jigsaw のお勉強 その3

Java

Jigsaw のお勉強の続きです。

超スローペースですがゆっくり慌てず三歩進んで二歩下がるような感じでもう暫く進めていこうと思っています。

今回は、最新Java情報局 「Java SE 9を先取り、Project Jigsawでモジュールを作成する」の写経をおこなってみました。

プログラムは同じではないのですがProject Jigsawで実現される機能の確認は同様にしました。

元記事で解説されているようにモジュールの定義は次のようになっています。

モジュールは複数のパッケージやリソースなどを含む、自己記述可能なコードの集合

今までの JARファイルにモジュール名が付いて依存性や公開範囲を記述できるようになったようです。

詳しくは元記事を参照してくださいませ。

では、モジュールを作っていきます。

プログラムを起動したら挨拶(文字列)を表示させるだけのプログラムです。

挨拶を表示させるためのインタフェースとしてGreetingインタフェース、その実装クラスとしてMultilingualGreetingクラスを作成します。

実装クラスのMultilingualGreetingクラスは日本語の挨拶か英語の挨拶をランダムに返します。

MultilingualGreetingクラスは外部からアクセスされることを避けるようにします。(Java SE 9 からインターフェイスにprivateメソッドが記述できるようになったのは今回は忘れてください。)

このアプリケーションをモジュールとするためにmodule-info.javaをトップディレクトリに作成します。

exports jp.yucchi.greeting;とmodule-info.javaに記述されています。

これは公開範囲を定義しています。

jp.yucchi.greeting.internalパッケージは公開されてないので外部からアクセスすることはできません。

それではjp.yucchi.greetingモジュールを使用するモジュールを作成します。

続いてこちらもモジュールとするためにmodule-info.javaを作成します。

先程作成したjp.yucchi.greetingモジュールを使用するためにrequires jp.yucchi.greeting;と記述しています。

これは依存性の定義となります。

面白いのはexports jp.yucchi.helloと自分自身を公開すると定義しています。

Helloクラスのmain()メソッドをコールすることも外部アクセスとなるからです。

これでシンプルなお試しプログラムが完成しました。

ディレクトリ構造は前回までと少し変更しています。

1

それぞれコンパイルしてみましょう。

binディレクトリを作成してコンパイルされたファイルを配置しました。

2

それでは確認のためにプログラムを実行してみます。

3

ちゃんとうごきましたね!(^_^)

それではモジュラーJARとしてパッケージ化してみましょう。

modsディレクトリを作成してモジュラーJARとしてパッケージングし配置しました。

4

一応できているみたいですが正しくできているか念の為に確認します。

5

モジュラーJARの依存性、公開範囲、メインクラスなどの情報に間違いが無いことを確認することができました。

依存性の定義にjava.baseモジュールがありますがこれはjava.lang.Objectクラスなどが含まれた必要不可欠なもので全てのモジュールに含まれることになります。

greeting@1.0.jarでは非公開パッケージがcontains jp.yucchi.greeting.internalと表示されています。

ここでJava SE 9からjarコマンドが少し変更されているのでヘルプをみておきましょう。

yucchi@ubuntu:~/Documents/MyJigsawProjects/HelloJigsaw$ jar –help
使用方法: jar [OPTION…] [ [–release VERSION] [-C dir] files] …
jarはクラスおよびリソースのアーカイブを作成し、アーカイブから個々のクラスまたは
リソースを操作または復元できます。

例:
# 2つのクラス・ファイルを含むclasses.jarというアーカイブを作成する:
jar –create –file classes.jar Foo.class Bar.class
# foo/のすべてのファイルを含む、既存のマニフェストを使用したアーカイブを作成する:
jar –create –file classes.jar –manifest mymanifest -C foo/ .
# モジュラjarアーカイブを作成する。モジュール・ディスクリプタはclasses/module-info.classに
# ある:
jar –create –file foo.jar –main-class com.foo.Main –module-version 1.0
     -C foo/ classes resources
# 既存の非モジュラjarをモジュラjarに更新する:
jar –update –file foo.jar –main-class com.foo.Main –module-version 1.0
     -C foo/ module-info.class
# 複数リリースjarを作成し、一部のファイルをMETA-INF/versions/9ディレクトリに配置する:
jar –create –file mr.jar -C foo classes –release 9 -C foo9 classes

jarコマンドを短縮または簡略化するには、個別のテキスト・ファイルで引数を指定し、
記号(@)を接頭辞として使用してjarコマンドに渡します。

例:
# 追加オプションおよびクラス・ファイルのリストをファイルclasses.listから読込みます
jar –create –file my.jar @classes.list

メイン操作モード:

  -c、–create               アーカイブを作成します
  -i,、–generate-index=FILE  指定のjarアーカイブの索引情報を
                             生成します
  -t、–list                 アーカイブの内容を一覧表示します
  -u、–update               既存のjarアーカイブを更新します
  -x、–extract              指定の(またはすべての)ファイルをアーカイブから抽出します
  -d, –describe-module      モジュール・ディスクリプタまたは自動モジュール名を出力します

どのモードでも有効な操作修飾子:

  -C DIR                     指定のディレクトリに変更し、次のファイルを
                             取り込みます
  -f、–file=FILE            アーカイブ・ファイル名。省略した場合、stdinまたは
                             stdoutのいずれかが操作に基づいて使用されます
      –release VERSION      次のすべてのファイルをjarのバージョニングされたディレクトリ
                             (つまり、META-INF/versions/VERSION/)に配置します
  -v、–verbose              標準出力に詳細な出力を生成します

作成または更新モードでのみ有効な操作修飾子:

  -e、–main-class=CLASSNAME モジュラまたは実行可能なjarアーカイブに
                             バンドルされたスタンドアロン・アプリケーションの
                             アプリケーション・エントリ・ポイント
  -m、–manifest=FILE        指定のマニフェスト・ファイルからマニフェスト情報を
                             取り込みます
  -M、–no-manifest          エントリのマニフェスト・ファイルを作成しません
      –module-version=VERSION    モジュラjarの作成時または非モジュラjarの更新時の
                             モジュール・バージョン
      –hash-modules=PATTERN モジュラjarの作成時または非モジュラjarの更新時に
                             指定のパターンに一致し、直接または間接的に
                             依存しているモジュールのハッシュを
                             計算および記録します
  -p、–module-path          ハッシュを生成するモジュール依存性
                             の場所

作成、更新および索引生成モードでのみ有効な操作修飾子:

  -0, –no-compress          格納のみ。ZIP圧縮を使用しません

その他のオプション:

  -h、–help[:compat]        これ(オプションで互換性)をhelpに指定します
      –help-extra           追加オプションのヘルプを提供します
      –version              プログラム・バージョンを出力します

モジュール・ディスクリプタ’module-info.class’が指定のディレクトリのルートまたは
jarアーカイブ自体のルートにある場合、アーカイブはモジュラjarです。
次の操作は、モジュラjarの作成時または既存の非モジュラjarの更新時に
のみ有効です:  ‘–module-version’、
‘–hash-modules’および’–module-path’。

ロング・オプションへの必須またはオプションの引数は、対応するショート・オプション
に対しても必須またはオプションになります。

Project Jigsawの影響でコマンドが増えていますね。(当たり前か・・・)

最後にこのプログラムを実行してみます。


java --module-path mods --module jp.yucchi.hello

6

アプリケーション・エントリ・ポイントを設定したモジュールのモジュールパスとモジュール名を指定してjavaコマンドによって実行します。

今回はモジュラーJAR作成時にアプリケーション・エントリ・ポイントを指定してますのでこのように実行できます。

モジュラーJAR作成時にアプリケーション・エントリ・ポイントを設定してない場合は次のように実行します。

モジュールのモジュールパスの指定はおなじです。

–module モジュール名/モジュールのメイン・クラス名とアプリケーション・エントリ・ポイント指定しなければいけません。

7

ついでだから javaコマンドのヘルプも確認しておきます。

 

yucchi@ubuntu:~/Documents/MyJigsawProjects/HelloJigsaw$ java –help
使用方法: java [options] <mainclass> [args…]
           (クラスを実行する場合)
   または  java [options] -jar <jarfile> [args…]
           (jarファイルを実行する場合)
   または  java [options] -m <module>[/<mainclass>] [args…]
       java [options] –module <module>[/<mainclass>] [args…]
           (モジュールのメイン・クラスを実行する場合)

メイン・クラス-jar <jarfile>、-mまたは–module
<module>/<mainclass>に続く引数は、メイン・クラスへの引数として渡されます。

オプションは次のとおりです:

    -d32      推奨されていません。今後のリリースで削除される予定です
    -d64      推奨されていません。今後のリリースで削除される予定です
    -cp <ディレクトリおよびzip/jarファイルのクラス検索パス>
    -classpath <ディレクトリおよびzip/jarファイルのクラス検索パス>
    –class-path <ディレクトリおよびzip/jarファイルのクラス検索パス>
                  :区切りリスト(ディレクトリ、JARアーカイブ、
                  ZIPアーカイブ)で、クラス・ファイルの検索用。
    -p <module path>
    –module-path <module path>…
                  ディレクトリの:区切りリスト、各ディレクトリ
                  はモジュールのディレクトリです。
    –upgrade-module-path <module path>…
                  ディレクトリの:区切りリスト、各ディレクトリ
                  は、ランタイム・イメージ内のアップグレード可能な
                  モジュールを置換するモジュールのディレクトリです
    –add-modules <module name>[,<module name>…]
                  初期モジュールに加えて解決するルート・モジュール。
                  <module name>には次も指定できます: ALL-DEFAULT、ALL-SYSTEM、
                  ALL-MODULE-PATH.
    –list-modules
                  参照可能なモジュールをリストし終了します
    -d <module name>
    –describe-module <module name>
                  モジュールを説明し終了します
    –dry-run     VMを作成しメイン・クラスをロードしますが、メイン・メソッドは実行しません。
                  –dry-runオプションは、次の検証に役立つ場合があります:
                  モジュール・システム構成などのコマンド行オプション。
    –validate-modules
                  すべてのモジュールを検証し終了します
                  –validate-modulesオプションは、次の検索に役立つ場合があります:
                  モジュール・パス上のモジュールでの競合およびその他のエラー。
    -D<name>=<value>
                  システム・プロパティを設定します
    -verbose:[class|module|gc|jni]
                  詳細出力を有効にします
    -version      製品バージョンをエラー・ストリームに出力して終了します
    –version     製品バージョンを出力ストリームに出力して終了します
    -showversion  製品バージョンをエラー・ストリームに出力して続行します
    –show-version
                  製品バージョンを出力ストリームに出力して続行します
    –show-module-resolution
                  起動時にモジュール解決出力を表示します
    -? -h -help
                  このヘルプ・メッセージをエラー・ストリームに出力します
    –help        このヘルプ・メッセージを出力ストリームに出力します
    -X            追加オプションのヘルプをエラー・ストリームに出力します
    –help-extra  追加オプションのヘルプを出力ストリームに出力します
    -ea[:<packagename>…|:<classname>]
    -enableassertions[:<packagename>…|:<classname>]
                  指定した粒度でアサーションを有効にします
    -da[:<packagename>…|:<classname>]
    -disableassertions[:<packagename>…|:<classname>]
                  指定した粒度でアサーションを無効にします
    -esa | -enablesystemassertions
                  システム・アサーションを有効にします
    -dsa | -disablesystemassertions
                  システム・アサーションを無効にします
    -agentlib:<libname>[=<options>]
                  ネイティブ・エージェント・ライブラリ<libname>をロードします。例: -agentlib:jdwp
                  -agentlib:jdwp=helpも参照してください
    -agentpath:<pathname>[=<options>]
                  フルパス名を使用して、ネイティブ・エージェント・ライブラリをロードします
    -javaagent:<jarpath>[=<options>]
                  Javaプログラミング言語エージェントをロードします。java.lang.instrumentを参照してください
    -splash:<imagepath>
                  指定されたイメージを含むスプラッシュ画面を表示します
                  HiDPIスケールのイメージが自動的にサポートされて使用されます
                  (可能な場合)。スケーリングされないイメージのファイル名(image.extなど)を
                  引数として-splashオプションに必ず渡す必要があります。
                  指定された最も適切なスケーリング済イメージが選択されます
                  (自動的)。
                  詳細は、SplashScreen APIのドキュメントを参照してください
    @argumentファイル
                  オプションを含む1つ以上の引数ファイル
    -disable-@files
                  さらなる引数ファイル拡張を無効にします
長いオプションの引数を指定する場合、–<name>=<value>または
–<name> <value>を使用できます。

まとめ

今回は何も考えずにほぼ写経しただけなので随分楽に進みました。

しかし、モジュール化のメリットの依存性、公開範囲の制限を定義することを学ぶことができました。(^_^)

module-info.javaにrequiresとexportsを記述して依存と公開の定義をする。

requires モジュール名;とすることで指定したモジュールに依存することを定義します。

今回は使っていませんがrequires transient モジュール名;とすることで推移的依存(依存したモジュールの依存先も依存可能)を定義できます。

また、requires static モジュール名;とすることでコンパイル時のみ依存可能と定義できます。

ちょっと複雑に感じますが慣れてくると便利に依存を定義することができそうですね。

次に公開するパッケージを定義するのにexports パッケージ名;と記述します。

exports パッケージ名 to モジュール名;とすることで特定のモジュールに公開することが可能です。

 

ゆっくり少しずつだけどお勉強を進めていこう。

Jigsaw のお勉強 その1

Jigsaw のお勉強 その2

Hatena タグ:

« 古い記事