JavaOne 2017 4日目レポート
遅くなろうが順番がめちゃくちゃだろうが、とにかく書くことが大事なんだ、と教わった。
JavaOne 2017の4日目です。この日はキーノート的なものは一切なかったので、ずっと個別セッションに出ていました。
Streams in JDK 8: The Good, the Bad, and the Ugly
スピーカーが、Simon RitterとStuart Marksというちょっと変わった組み合わせ。余談だけど、Simon Ritterの英語は早口なのにすごく聞き取りやすかった。
Stream APIを使った(あるいは使うべき)コードを見て、何がいけないのか、どう直すべきなのかを説明していくセッションです。Good/Bad/Uglyのセッションは、説明が明確で分かりやすいから好きです。
たとえば、
action=validate&key=10&key=22&key=10&key=30
みたいなURLパラメータをパースして、
action,validate
key,[10,22,10,33]
みたいな結果を取り出すコードを考えてみよう、という感じ。それで、UglyとかBadなコードを見せて、何がいけないのかを指摘する。
で、だいたい最初に悪いコード例が出される。
基本的な指摘としては
- Functionalに考える
- 1つの中間操作とか末端操作の中に普通に複雑なロジック入れるのはStreamの意味がない
- stateによって処理を分けるようなののも避けよう
- 中間処理でprintlnで出力結果出して利用するのは良くない
- Internal IterationとExternal Iterationをちゃんと使い分ける
- 処理速度に注意する
みたいな感じ。
あと、Parallel Streamについては、以下のような感じの指摘でした。
- Parallelにしたからといって、必ずしも速くなるわけではない
- Parallel Streamsはcommon fork-join poolを利用しているので、その挙動を把握しよう
- デフォルトのスレッド数=CPUのコア数。コンテナ環境とかは特に注意
- ネストすると遅くなることが多い
Modules and Services
Java9のモジュールについて、サービスプロバイダの仕組みを解説。要するに、SPIの仕組みがモジュールが導入されてどうなったか的な感じの話でした。
モジュール定義にはrequiresとexportsの他にusesとprovideっていうキーワードがあって、使いたいサービスプロバイダを指定する場合(利用側)にはusesを、サービスの実装を提供する場合(提供側)にはprovideを使う。
この場合だと、java.desktop側からは直接FirstPrintにはアクセスできなくて、必ずPrintServiceLookup経由で使うことになる。コード上はインタフェースが指定されていて、使う実装は実行時に決まる、いままでのSPIと同じ動き。
java.desktopモジュールではこのサービスタイプをたくさん使ってる。
あと、Optionalとかも使えるっていう話がありました。
55 New Features In JDK 9
ふたたびSimon Ritter。JDK 9の新機能をズラーっと説明していくセッション。ほぼ既知の内容でした。
というか、久保田さん(@sugarlife)が去年のJJUG CCCでやったセッションでほぼカバーされてる。さすがです。
G1GC Concepts and Performance Tuning
G1GCの基本的な動きと、それをどうやってチューニングしていくかといったポイントの解説。
基本的には、GCのオプションつけて、ダンプして、Flight Recorderで見て、それを元にチューニングするという話。
ちょっとすぐにはまとめきれないので後回し。
The G1 GC in JDK 9
上のセッションと同じスピーカー。やる順番が逆のような気がした。
前半は、G1GCの基本的な仕組みをもっと詳しく解説、後半はJDK8からJDK9までに何が変わったかという話。
大きな変更点は以下の5つ。
- String deduplication (8u20)
- Class unloading with concurrent mark (8u40)
- Eagerly reclaim humongous regions (8u60)
- Adaptive start of concurrent mark (JDK9)
- More efficient collections (JDK9)
実質1つじゃんってツッコミは置いといて。
1は、2つの同値(equals()の結果がtrue)のStringオブジェクトがある場合に、その中身の参照先を同じchar[]にする。メモリは大幅に節約できる反面、CPU使用率は少し上がってYoungGCは少しだけ伸びる。
2は、オブジェクトが不要になったかどうかのマーク処理を並列で実行するというもの。fall-back full GCよりも処理は複雑でトリッキーだけど高速。
3は、1つの領域の半分を超えるようなでかいオブジェクトがあった場合、humongous領域に独立して格納する。これによって参照が無くなった段階で簡単に解放できるようになる。humongous領域は他の領域とは独立しているから、並列マークが完全に終わるのを待たなくても解放できる。
4は、Old領域の並列マークの開始タイミングを最適化する仕組みの導入。Old regionの並列マークは領域がフルになる前に完了しないといけないので、フルにならないようにするための安全なマージンが必要。一方でフルGCはギリギリまでやりたくない。そのマージンをどれくらい取るのかを実行時のデータを元にして最適化する。
5は、上記の他にもいろいろ改善されていて、実に250以上のenhancementsと180以上のbug fixesがあったよ、というお話。
あと、次のJDKに向けて開発中の機能として、パラレルfall-back full GC(JEP 307)とか、card scanningの高速化なんかが進められているらしい。
Troubleshooting Memory Problems in Java Applications
Javaアプリケーションのメモリエラーに関するトラブルシューティングのセッション。
OutOfMemoryErrorが起こったときに、その原因を探って、解消するための方法を解説。基本的には、GCのログとヒープダンプをとってちゃんとツールで追っていけば、怪しい箇所が見つかるよ、という話。あと、どういうときにメモリリークが起こりやすいか、何を見ればそれがわかるかなどが詳しく紹介されました。
Javaのメモリの問題は、おもに次の原因で起こる。
メモリリークの解析や解消を手助けするツールやオプションは以下のようなものがある。