torutkのブログ

ソフトウェア・エンジニアのブログ

Java読書会BOF「Javaパフォーマンス」を読む会(第4回)を開催して #javareading

通算198回目のJava読書会を本日開催しました。再来月は200回記念です。

川崎市の団体登録更新

Java読書会BOFは、主に川崎市の公共施設の会議室を借りて開催しています。川崎市の会議室を借りるには、川崎市の「ふれあいネット」と呼ばれるシステムに利用者登録(団体登録)をする必要があります。登録をすると、インターネットや各公共施設に設置の端末から会議室の抽選申込、検索、予約ができるようになります。
Java読書会BOFは、この「ふれあいネット」に団体登録をして会議室を予約・使用して読書会を開催しています。
https://www.fureai-net.city.kawasaki.jp/

川崎市の場合は、団体登録には代表者を含めて5人以上の構成員名簿の登録が必要です。代表者を含めて半数以上が川崎市内在住・在勤・在学の場合は市内団体として抽選申込が可能になります。市外団体となると抽選参加ができず、空きがあれば予約できるという制限があります。

会議室の使用料金は施設によりますが安い価格のところでは、午前・午後の時間帯で合わせて2000円〜3000円台となっています。

有効期限が3年間なので、3年に1回更新手続きが必要になります。Java読書会BOFは今月がその期限となっていたので、本日ちょうど更新手続きを実施しました。

Javaのオブジェクトがヒープ上で消費するメモリサイズ


7.2.1 オブジェクトのサイズを減らす(p.201-)において、オブジェクトのヘッダーについて次のように記述があります。

通常のオブジェクトでのヘッダーのサイズは、32ビットJVMでは8バイト、64ビットJVMでは(ヒープサイズにかかわらず)16バイトです。

とありますが、64ビットJVMでヒープが32GB未満なら圧縮OOPが有効になるので12バイトになると思われます。実際、次ページの表7-2でint型のフィールドを1つ定義したクラスAの浅いサイズを16バイトとしているのは、オブジェクトヘッダー12バイトとint型フィールド4バイトを加算した結果です。もしオブジェクトヘッダーが16バイトなら、int型フィールド4バイトを加算した20バイトを8バイトアライメントになるようパディングを追加して24バイトになっているはずです。

ArrayListの引数なしコンストラクタでは要素を格納する配列を初期化しない

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData; 

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

ダブルチェックロッキングはかえって効率が悪くないか?

p.208のサンプルコード

private volatile ConcurrentHashMap instanceChm;

public void doOperation() {
    ConcurrentHashMap chm = instanceChm;
    if (chm == null) {
        synchronized(this) {
            chm = instanceChm;
            if (chm == null) {
                chm = new ConcurrentHashMap() {
                    ...
                instanceChm = chm;
             }
         }
     }
     ... chmを使った処理
}

毎回volatile変数を参照するとメモリバリアが発行されます。昨今のJavaVMは、Biased lockが有効なので、普通にsynchronizedで書いたほうがダブルチェックロックよりも効率がよくなるのではないかなと思います。コードも見通し悪いですし。

ThreadLocalRandom

Java SE 7から追加されたAPIです。
従来のRandomクラスはスレッドセーフですがnext()メソッド呼び出しで競合が発生し性能低下するのに対して、ThreadLocalRandomは競合がないのでマルチスレッドでRandomを使用するときはThreadLocalRandomがよいとのことです。

ちなみに、Randomクラスの中を覗くと、nextDoubleメソッドはnextメソッドを2回呼び出し、このnextメソッドではAtomicLongとCAS命令を使ったスレッドセーフ実装をしていました。(JDK 8u60付属のソースコードで確認)

NetBeans IDEのメモリプロファイル

p.192で、jcmdおよびjmapコマンドでヒープダンプを生成した際、そのヒープダンプを読み込み解析するヒープダンプ解析ツールとしてjhat、jvisualvm、mat(Eclipse Memory Analyzer Tool)の3つが紹介されていました。

jhatは、OpenJDK 9では、廃止予定となっているのでこれから使うにはあまり推奨できないかと。
http://openjdk.java.net/jeps/241

書籍「Javaパフォーマンス」ではなぜか取り上げられないNetBeans IDEですが、標準でメモリプロファイル機能を搭載しています。NetBeans 8.0を起動し、[プロファイル]メニュー > [ヒープダンプをロードする]でヒープダンプファイルを選択すれば内容を解析し、また別なヒープダンプとの比較も可能です。

実行中のJavaプロセスにアタッチしてライブにヒープ解析も可能です。

NetBeans IDEでのJavaアプリケーションのプロファイリングの概要