読書会(Javaパフォーマンス)第4回議事録

[ 戻る ]


============================================================
Java読書会BOF 「Javaパフォーマンス」を読む会 第4回
============================================================
.. csv-table:: 開催概要

   "日時","2015年9月26日 10:00 - 17:00"
   "場所","川崎市教育文化会館 第3会議室"
   "出席者(敬称略)","高橋(徹)、高橋(智)、遠藤、後藤、ウェイ、小棚木、吉本、川崎、今井、平山、村山、岩室(書記)"

6章 ガベージコレクションのアルゴリズム
============================================================

6.4 高度なチューニング
------------------------------------------------------------

6.4.2 大きなオブジェクトの割り当て
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

6.4.2.2 TLABのサイズ変更
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* TLABとは何か?
    * TLAB = Thread Local Allocation Buffer
        * Young領域にはEdenとSurvivorがあるが、Edenの一部にはさらにTLABという領域が割り当てられている。
        * TLABが使い尽くされるとEdenから直接メモリが割り当てられる。
    * 参考: Javaガベージコレクションのエッセンス
      http://www.infoq.com/jp/articles/Java_Garbage_Collection_Distilled

* TLABに関するデフォルト設定は以下の通り:
    * -XX:TLABSize=0
    * -XX:+ResizeTLB

6.4.2.3 超巨大オブジェクト
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

6.4.2.4 G1でのリージョンサイズ
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* p.181の1行目: 微妙に分かりづらいが、単に2のべき乗に丸めているだけ。

6.4.2.5 G1での超巨大オブジェクトの割り当て
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* 「超巨大」とは書かれているが、下限は500KBと意外に小さい。

* Javaにおいて巨大オブジェクトが生成される状況は?
    * byte等の配列や文字列を生成したとき。
    * 普通のオブジェクトの場合、基本的にリファレンスの集まりなので、メモリ消費は大きくなってもオブジェクト自体が巨大というわけではない。
    * フィールドが多数あるとオブジェクトが大きくなることはある。
        * フィールドの個数は最大65535。(64bit/ヒープサイズ32GB以上のときにほぼ最大値近くまで持たせてようやく500KBに届く)

* 巨大なオブジェクトを生成したい状況ならば、DirectBuffer(p.242参照)を使う方がよいと思われる。

6.4.3 AggressiveHeap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* 表6-4: Mmx上限が160MBなのは、Aggressiveと言いつつ少ないのではないか?
  ⇒ 昔からあるオプションなので、当時の環境が基準になっているのかも。

* 何をもって「aggressive」と表現しているのか?
  ⇒ GCのオーバーヘッドを下げる方向性(対GCベンチマーク?)ではないか?

6.4.4 ヒープサイズの完全な制御
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* p.186: 「それ4メガバイト」⇒「それぞれ4メガバイト」

* メモリ上限はJava8では変わっている?
  ⇒ 「java -XX:+PrintFlagsFinal」を見る限りでは変わってなさそう?

6.5 まとめ
------------------------------------------------------------

7章 ヒープのベストプラクティス
============================================================

7.1 ヒープの分析
------------------------------------------------------------

7.1.1 ヒープヒストグラム
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* jcmdとjmapではデータの取得方法が違う?
    * jcmdはルートからトラバースしていて、jmapは頭からスキャンしているのではないか?
        * jcmdの方が取得が遅い可能性がある。(宿題)

7.1.2 ヒープダンプ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* 「保持メモリ量」の定義上、キャッシュ等で共有されているオブジェクトについては、保持メモリ量の値はあてにならないと思われる。

* jhatが正しく動かない経験をした人が意外と多い模様。

* jvisualvmはsshポートフォワーディングで使用できるか?
    * JVMオプションを設定すれば使える模様。
        * 参考1: VisualVMをリモート接続しよう(Firewall越しに)
          http://tech.furyu.jp/blog/?p=3467
        * 参考2: JMXでのリモート接続でハマったこと
          http://d.hatena.ne.jp/Kazuhira/20141112/1415782056
    * ただし、com.sun.management.jmxremote.rmi.port プロパティが追加されたのはJava7u4のため、Java6では使えない? (バックポートされている可能性もあるため、要確認)

* matは本日の参加者で使用経験がある人はいない模様。

7.1.3 OutOfMemoryError
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

7.1.3.1 ネイティブメモリがいっぱいの場合
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* 32bit環境では、実際にはもっと小さいサイズしか使えないことが多い。
    * 例えば、Windowsではヒープサイズの上限は1GBを超える程度。

7.1.3.2 permanent領域やメタスペースがいっぱいの場合
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

7.1.3.3 ヒープメモリがいっぱいの場合
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

7.1.3.4 ガベージコレクションに時間がかりすぎる場合
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

7.2 メモリの使用量を減らす
------------------------------------------------------------

7.2.1 オブジェクトのサイズを減らす
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* オブジェクトヘッダのサイズは、厳密には以下の通り。
    * 32bitVM: Mark Word: 4バイト + Klass Word: 4バイト = 8バイト
    * 64bitVM(ヒープサイズ<32GB): Mark Word: 8バイト + Klass Word: 4バイト = 12バイト
    * 64bitVM(ヒープサイズ≧32GB): Mark Word: 8バイト + Klass Word: 8バイト = 16バイト

* ただし、アラインメントは常に8バイトのため、64bitVMの場合ヒープサイズにかかわらず少なくとも16バイト消費する。

* 参考: Javaオブジェクトのメモリ構造 https://www.infoscoop.org/blogjp/2014/06/16/java-objects-memory-structure/
    * 元記事の「Code Instructions: Java Objects Memory Structure」はドメインごと消滅している模様。

* 表7-1からbooleanが漏れている。(booleanは1バイト消費)

* 状態を表現する場合、通常はenumを使うのでは?
    * メモリ効率を考慮しなければならないときはbyteにした方がよい?

7.2.2 オブジェクトの初期化を遅らせる
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* p.205のサンプルコード: Calendarオブジェクトの初期化タイミングが上と下で異なるので、getTime()の結果も変わるのでは?

* P.206 ArrayListの例: これは「遅延」初期化とは言えないのでは? 空配列も値としては正当なデータのはず。

* p.207中段のサンプルコード
    * スペルミスがある: unsychronizedCalendarInit → unsy【n】chronizedCalendarInit
    * unsynchronizedCalenderInitがスレッドセーフでないならば、dfは異なるインスタンスを指す可能性があるが、それならばdfに関してはスレッド間での同期は必要ない。dfが同じインスタンスを指すならば、synchronizedによりメモリの同期が行なわれるので、不完全な状態の
DateFormatオブジェクトが見えることはない。     * が、calendarは同期化されていないので、複数のスレッドでcalendarが指しているオブジェクトが同じとき、calendarオブジェクトがそれぞれのスレッドで正しく見える保証はないのではないか?
    * マルチスレッドで遅延初期化すると、たいていの場合ろくなことにならなさそう。

* p.208中段のサンプルコード: 「chmを使った処理を行います」の部分は、ifブロックの外側に置く必要がある。今の位置だと、chm == nullのときにしか動かない。

7.2.2.1 事前の解放
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* p.208のサンプルコードについて
    * biased lockingにより、synchronizedによるパフォーマンス低下は、同一スレッド内ならば大きなものではない。
    * valatileはメモリバリア命令が発行されるため、パフォーマンスへの影響が大きいのではないか。
    * よって、double-checked lockingを用いるよりも単純なコードを書いたほうが、可読性もパフォーマンスも向上すると思われる。(= double-checked lockingを使う意味がない)

* p.209サンプルコード内のコメントの翻訳: オリジナルのコメントは「clear to let GC do its work」なので、翻訳は適切とは言えない。

7.2.3 immutableオブジェクトとcanonicalオブジェクト
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

7.2.3.1 canonicalオブジェクトの作成
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* p.211のサンプルコード: mapの定義部分は正しくは以下のように記述する必要がある。
    * WeakHashMapの2つ目の型引数はImmutableObjectではなくWeakReference<ImmutableObject>。
    * コンストラクタに「<>」を付けないとraw型になるためコンパイラが警告を出す。

.. sourcecode:: Java

    private static WeakHashMap<ImmutableObject, WeakReference<ImmutableObject>> map = new WeakHashMap<>();

* WeakReferenceとSoftReferenceはどちらを使うべきか?
    * GCをまたいでもなるべく残したい場合はSoftReferenceを用いる。
    * ただし、SoftReferenceはGCに負担がかかるため(後述)、使い所に注意する必要がある。

7.2.4 Stringのintern化
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* internされた文字列は何処に格納されるのか?
    * Java6以前はpermanent領域。
    * Java7以降はヒープに移されたらしい。

* intern化された文字列はGCされるのか?
    * されるらしい。
    * http://stackoverflow.com/questions/18152560/garbage-collection-on-internd-strings-string-pool-and-perm-space にそれらしいことが書いてある。
    * http://java-performance.info/ に情報があるらしいが見付けられず。

* internされた文字列に関するJVMオプションでチューニングはできるが、JVM全体で一つのテーブルのため、シンボル的なユースケースが必要な場合は独自のシンボルテーブルを実装した方がよいのではないか。

* internはclass loaderをまたがるか?
    * JVM全体で一つのテーブルのため、class loader間でも共有される。

7.3 オブジェクトのライフサイクル管理
------------------------------------------------------------

7.3.1 オブジェクトの再利用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

7.3.1.1 オブジェクトプール
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

7.3.1.2 スレッドローカル変数
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

7.3.2 弱い参照、ソフト参照、その他の参照
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

7.3.2.1 ソフト参照
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. note:: 次回は「7.3.2.2 弱い参照」から。

(以上)


[ 戻る ]