java.lang.Object#hashCode()の性質という記事で書いたのですが、Java の Object#hashCode() の値はただの乱数となっています。
この乱数のアルゴリズムが、Java SE 8 で「線形合同法」から「XORシフト方式」に変更になっていました。
といっても、変更されたのはたった1文字。
VMオプションのデフォルト設定が -XX:hashCode=0 から -XX:hashCode=5 に変わっただけでした。
hotspot-rt Udiff hotspot/src/share/vm/runtime/globals.hpp
どういうこと?
もともと、Java の以前の実装*1 *2から、Object#hashCode() のアルゴリズムはVMオプション -XX:hashCode=? で選べるようになっていました。
ですが、デフォルトは長いこと 0(=線形合同法による乱数) になっていました。
値 | 挙動 |
---|---|
0 | 線形合同法による乱数(デフォルト) |
1 | オブジェクトのアドレス+乱数 |
2 | (常に)1 |
3 | 連番 |
4 | オブジェクトのアドレス |
その他 | XORシフト方式による乱数 |
しかし、Java SE 8 をリリースするにあたって改めて調べたところ、「-XX:hashCode=5(XORシフト方式)に変えたほうがマルチスレッドでのパフォーマンスが格段に良い*3」という結果が得られたそうです。
既存のプログラムに影響があるかもという慎重な意見もありましたが、影響があったら -XX:hashCode=0 を指定すればいいだけだということで、最終的に変更することになったようです(英語苦手なので、意味あってるか自信ないですが…)。
RFR (XS): CR 8006176: Switch to optimal identity hash code generator
ささやかな変更ながら、Java 8 でこういう細かいところまで手が加えられているのが分かって、ちょっと面白かったです。
*1:少なくとも OpenJDK の最初のコミットから存在していました。http://hg.openjdk.java.net/jdk6/jdk6/hotspot/annotate/a61af66fc99e/src/share/vm/runtime/synchronizer.cpp#l271
*2:この実装がされたとき、既にソースコードのコメントで「This is probably the best overall implementation -- we'll likely make this the default in future releases. (これはおそらく全体的に最良な実装です。我々はおそらく、将来のリリースでこれをデフォルトにします。)」という記載がされていました。
*3:該当のコードをざっと見た感じ、アルゴリズムの優秀さもありますが、乱数シードを各スレッドごとに独立して持っているというのもマルチスレッドでのパフォーマンス向上につながっているようです。