あどべんとかれんだーかっといん
('ε’ )くーー
('ㅂ’ ) り
('ε’ )す
('о’ )ま
('ε’ )す
('о’ )が
('ロ’ )こ
('ロ’ )ろ
('ㅂ’ )し
('ㅂ’ ) に
('о’ )やーっ
('ㅂ’ )て
('ε’ )く
('ε’ )るーー
CMS GC おさらい
この記事は Java Advent Calendar 2014 の一日目の記事です。
先日の JJUG CCC 2014 Fall で CMS GC について話してきました。
結構遅めの時間帯にも関わらず、200人規模の部屋がいっぱいに埋まるぐらいの盛況振りで、みなさんGCにお困りなんだなあと実感しました。スライドは以下に公開しています。CMS GC の挙動から GC ログの読み方、どういうケースが厄介なのかを紹介しているので是非ご覧ください!
嬉しいことにセッションの反応は良かったのですが、「遅めの時間帯で頭も疲れてるとガチ話辛い」という声もあったので、今回は CMS GC について比較的重要な点についてだけ簡単におさらいしたいと思います。
オプションについて
@nekop さんの「Java 7 CMS GCの基本的な情報の整理 - nekop's blog」を読めば OK 。
上の記事から追記するとしたら
- -Xmx と -Xms は同じ値にしたほうが、拡張/シュリンク時に Major GC などの負荷が発生しないのでお勧め。
- -XX:+CMSClassUnloadingEnabled は JDK8 でデフォルト True なので、もう True で良いと思う。
他、CMS GC の発生条件のチューニングとして、以下のスライドで CMSInitiatingOccupancyFraction と UseCMSInitiatingOccupancyOnly を紹介しました。
UseCMSInitatingOccupancyOnly を使わずに、チューニングすることも可能ですが、自分が知っている限りでこれが無効の場合の CMS GC 開始条件は、
- CMS 実行間隔の統計的に、今実行しないとヒープが不足すると判断された場合
- 最初の CMS 実行時に、ヒープ使用率が CMSBootstrapOccupancy で指定した値を超えていた場合
- ヒープ領域拡張した結果、GC を行わなくてもメモリ確保に成功した場合
- 断片化によってまとまったメモリ空間の確保に失敗すると判断された場合
と、これ以外(インクリメンタル CMS やパーマネント領域関係とか)にもあるのでここら辺を一つ一つやるよりは UseCMSInitiatingOccupancyOnly で調整したほうが楽です。
GC ログの読み方について
GC ログの読み方はスライドの P.37 以降に一つ一つ書いているのでそちらで!
特に重要そうな点は以下の通りです。
おわりに
CMS GC ではパフォーマンス的にどうにもならない時もあります。どうにもならないことが掴めたら G1GC や Parallel GC、Azul Zingを使うことを検討しましょう。
これ以外にも OpenJDK にはまた新しい GC として G1GC リプレース型の Pauseless GC である Shenandoah がやってきます。JavaOne2014 でも発表していましたが、資料はこっちの方(「Shenandoah An ultra-low pause time Garbage Collector for OpenJDK」)が解りやすいかな。Remembered set絡みの処理が早くなるアイデアで、G1GC より停止時間が優れてる感じっぽいと受け取りましたが、出てくるのはしばらく後ですね。こんな感じで GC との付き合いはしばらく続きそうです。
Java 起動(Launcher)の仕組み
※:この記事は下書き中に本文ががっつり消えたため、知らずに部分的に端折ってるところがあるかもしれません。(´;ω;`)
Java、すなわち JVM (HotSpot) を立ち上げた時、どういった処理が行われているのでしょうか。正確に知りたい場合は OpenJDK のソースコードを読むのが最も確実ですが、概要レベルでどのような処理が行われていて、それがソースコードのどのあたりに書かれているのか案内があった方がすんなりと理解できます。と言うわけで、自分用のメモ書きをちょっとだけ整理してここで公開してみます。
なお、自分の理解をベースに記述しているので間違いが含まれている可能性があります。見つけた場合はそっとコメントか @sugarlife にお教え頂けると大変喜びます。
Java の動作概要について
Java、特に HotSpot の動作概要については、OpenJDK コミュニティによって「HotSpot Runtime Overview」という記事で解説されています。Java の起動時の処理についても「VM Lifecycle」という項目で説明されています。動作の概要を知りたい場合はこれらの記事を読むだけで理解できます。
和訳については有志らが取り組んでいる状況です。Java の動作に興味がある人は是非和訳をしてみませんか:)
HotSpot Runtime Overviewのコマンドライン引数とVMのライフサイクルを訳した。 http://t.co/LjNHEBVghW
— ╭( ・ㅂ・)و ̑̑ グッ ! (@tan_go238) 2014, 11月 2
この活動を見て、自分もこのメモ書きをちょっと手直しして公開してみようと思った次第です。
Java の起動 (Launcher)
HotSpot Runtime Overview で書かれている起動時における処理内容としては Launcher と JNI_CreateJavaVM が書かれています。Launcher というのは、ユーザが利用する java コマンド等のコマンド群 (java, javac, etc... ) 、およびそこから呼ばれるライブラリ (JLI、libjli) 部分を指します。「Launcher」とだけ書かれていた場合は、java コマンド等のコマンド群を指すことが多いです。
JNI_CreateJavaVM は The invocation API と呼ばれる API の一つで、JVM(HotSpot) そのものをロード & 初期化するメソッドです。現在の実装では一つのプロセスに複数の VM は作成不可能なため、一度のみ実行されます。
ここでは主に Launcher 部分について触れて行きます。なお、ソースコードへのリンクは jdk8 の tip(最新コミット) にリンクしています。jdk8 の最新版ではないことに注意してください。最新版は jdk8 の部分を jdk8u に変えることで確認できます。
- java コマンドを叩く
- main() / WinMain() [jdk/src/share/bin/main.c]
- JLI_Launch() [jdk/src/share/bin/java.c]
- LoadJavaVM() より libjvm をロード。Linux の場合は libjvm.so ファイルをダイナミックロード。
- HotSpot Runtime Overview に記載されている Launcher の 1. ~ 4. 部分が実行される。
- ContinueInNewThread() [jdk/src/share/bin/java.c]
- ContinueInNewThread0() [OSによって異なる]
- non primordial thread を作成、実行する。non primordial thread のエントリポイントは JavaMain()
- JavaMain() [jdk/src/share/bin/java.c]
- HotSpot Runtime Overview に記載されている 5. 以降の処理が行われる。
- (ContinueInNewThread0() で non primordial thread を作成し、)JNI_CreateJavaVM()を呼んで VM を作成・初期化する。新しいスレッド(non primordial thread)を作成しているのは、この処理自体を行っているスレッド(primordial thread)が、環境によっては色々な制限があり、VMの作成上で制約(Windows上でのスタックサイズなどによる制約など)があるため。
- InitializeJVM() が呼ばれ、JNI_CreateJavaVM() が呼ばれる。
- メインクラスを読み込んで、メインクラスからメインメソッドの属性を取得する。
- LoadMainClass() 以降の処理。メインメソッドを取得しているのは GetStaticMethodID()。
- CallStaticVoidMethod() とコマンドラインに渡された引数を使用してメインメソッドを呼び出す。
- メインメソッドが完了したら、待機中の例外 (pending exception) がないか確認し、exit status が渡されてないことを確認する。例外は ExceptionOccurred() でクリアされる。メソッドが成功していれば戻り値は 0 に、そうでなければ呼び出し元のプロセスに返却される。
- メインスレッドは DetachCurrentThread() によりデタッチされる。これによりスレッドカウントをデクリメントし、DestroyJavaVM() を安全に呼び出し、且つこのスレッドが VM 内の操作やスタック上に Java フレームが存在しないことを保証できる。
- JavaMain()は最終的に LEAVE() マクロを呼び出し、DetachCurrentThread()、DestroyJavaVM()を呼んでいる。
以上、Launcher 編でした。
JNI_CreateJavaVM() について。
ここは真面目にやると深いので、
- HotSpot Runtime Overview を読みましょう。
- 和訳を読みましょう。
- API ドキュメントを読みましょう。
- ちなみにソースコードの場所は慣れてないと解りづらいですがここです。読みましょう。
- 個人的に重要だと思っている一つは、Universe::initialize_heap()。ここで設定した GC アルゴリズムに基づいたヒープの初期化が行われます。
まとめ
OpenJDKこわい。
JJUG CCC 2014 Fall で Concurrent Mark & Sweep Garbage Collection について話します。良かったらマサカリを置いて来てね!
JavaOne 2014 で発表してきた。
JavaOne2014 で JVM 監視・トラブル解析支援ツールである HeapStats について発表してきました。
Session ID: BOF3108
Session Title: Troubleshooting with Serviceability and the New Runtime Monitoring Tool: HeapStats
Venue / Room: Hilton - Continental Ballroom 5
Date and Time: 9/30/14, 20:00 - 20:45
発表スライドは こちら からダウンロードできます。色々と課題も見えた発表でしたが、自分の関わっているプロダクトがあの JavaOne で名乗りを上げ、なおかつ(同時に行った出展なども通じて)好反応を得られたのは感無量でした。
発表や出展の様子は今週末の10/18(土)に行われる JJUG 主催の JavaOne 2014 サンフランシスコ報告会 Tokyo でもう一人のスピーカーが発表する予定です。
もし HeapStats についてコメントや質問、要望などがありましたら メーリングリスト に是非投稿お願いします:) 発表や説明して欲しいなーという方は @sugarlife に Mention ください!ステッカーばらまきに行きます!
JDK 8 新機能ダイジェスト (JDK 8 Features) #java
この記事は Java Advent Calendar 15 日目です!
14日目は @megascus さんの
動的SQLでPreparedStatement+プレースホルダを使用する - 水まんじゅう でした。
明日は @cero_t さんです。
まえがき
いよいよ JDK 8 が来年の3月に正式リリースされます。素晴らしいことに Java Japan User Group の皆様の blog や記事、 JJUG Night、CCC などの勉強会やカンファレンスなど、JDK 8 の新機能が紹介されている場が多いので、既にある程度知っている人も多いのではないでしょうか。しかし、JDK 8 全体としてどのような機能が加わるのか、それを把握する方法がよく解らない人もいらっしゃると思います。ここではどうすれば JDK 8 の新機能を確認できるのか、そして新機能はどう言ったものがあるのか簡単なダイジェストを紹介します。
なお、JDK 8 から導入されるクラスやメソッドは何かという観点での変更点確認は torutk さんの記事に纏まっていますので、そちらを参考に!
JDK Enhancement Proposal
Java 関連の周辺技術標準化は JCP(Java Community Process) によって行われ、新しい技術仕様や改訂仕様(既存技術仕様の改訂)は JSR (Java Specification Request) として提案され、標準化に関する作業が管理されます。
では、JSR を追って行けば JDK の新機能が解るのか?実はそれは違っていて、JDK で何かしらの新機能を加える場合、その内容は JEP (JDK Enhancement-Proposal) として管理されています。JDK 8 の機能部分は、Oracle 社独自の変更(解りやすい例としてFlight Recorder周り)以外の変更点は、OpenJDK 8 プロジェクトの Features から確認できます。今回はこの JEP についてダイジェストをまとめてみました。
JDK 8 Features ダイジェスト
と言うわけでダイジェストです。
126 Lambda Expressions & Virtual Extension Methods
JDK8 の目玉の一つ、Lambda。@bitter_fox さんが素晴らしい発表をJJUG CCC 2013 Fallでされたのでそちらを確認しましょう。
138 Autoconf-Based Build System
ビルドシステムの簡素化&並列化を目指して autoconf (./configure style) ビルドセットアップの導入、Makefileのリファクタなど。エンドユーザ(※ここではJava言語を使ってコード書く人)は関係なし。JVMをビルドする人は楽になる(かも)。
個人的にはビルドシステムの改善はどこのプロジェクトでも課題になりやすい感がありますね。
160 Lambda-Form Representation for Method Handles
Lambda関係。メソッドハンドル / invokedynamic の改善。JSR 292の延長。
161 Compact Profiles
Profileの導入。リソースが制限されたデバイスでも動作するように各用途に応じたプロファイルを準備できるようにする。
これは Java ME との統合の一環(JDK8からJava SEとJava MEの統合が目指されている)の拡張。
162 Prepare for Modularization
Project Jigsaw (モジュール化) の前準備。Jigsaw は 元々 JDK 8 からの予定だったが、JDK 9 にずれ込んだ。
164 Leverage CPU Instructions for AES Cryptography
174 Nashorn JavaScript Engine
Nashorn. Rhino 後継の JVM ベースの JavaScript 実装。
176 Mechanical Checking of Caller-Sensitive Methods
セキュリティ関連。エンドユーザは余り関係なし。
142 Reduce Cache Contention on Specified Fields
マルチスレッドアプリにおけるキャッシング改善。JVMレイヤの変更で、アプリレイヤは無関係(影響なし)。
122 Remove the Permanent Generation
パーマメント領域の削除。ヒープ領域への統合。
以下、このJEPのソースコードからの確認内容。
PermGenがなくなったので、クラスのアンロードはクラスローダのライフサイクル(破棄)と同期するようになる。
他、UseCompressedKlassPointers が true の場合(追記:デフォルトは環境依存)は、ClassMetaspaceSize(デフォルト100M。指定する場合はKlassEncodingMetaspaceMax以下にしないとUseCompressedKlassPointersが false になる)のMetaspaceを作成する。ここら辺の各種オプション等は、GA 出てから再確認してから別途掲載します。
ソースコード等の情報は以下のように取得する。
173 Retire Some Rarely-Used GC Combinations
テストやメンテナンスコスト削減のため、滅多に使われてない GC 組み合わせを削除。
具体的には
- -XX:-UseParNewGC -XX:+UseConcMarkSweepGC
- -XX:+UseParNewGC
- -Xincgc / -XX:+CMSIncrementalMode
を実行した場合、警告が出力される。
136 Enhanced Verification Errors
JVM's bytecode verifier の拡張。(VerifyError)
148 Small VM
3mb以下のvm作成をサポート。JavaME統合との一貫。
171 Fence Intrinsics
メモリフェンスintrinsics。エンドユーザには影響なし。省略。
103 Parallel Array Sorting
java.util.Arrays に配列をパラレルにソートするメソッド(parallelSort)を追加。JSR 166 (Fork/Join) 関連。
107 Bulk Data Operations for Collections
Lambda関連。ざっくり言うと「filter/map/reduce for Java」
109 Enhance Core Libraries with Lambda
Lambda。
112 Charset Implementation Improvements
Charsetの改善。目的はメンテナンス性と性能。
119 javax.lang.model Implementation Backed by Core Reflection
コアリフレクションによるjavax.lang.model実装(読んで字のごとく)。
149 Reduce Core-Library Memory Usage
性能に問題を与えない範囲でのコアライブラリによるメモリ消費量の削減。
150 Date & Time API
新たな日付APIの提供。詳細は kis さんの記事が参考になります。また、このAPIの象徴的なツイートは以下の通り。
Java Date Time API 橋本さん「既存Dateクラスとの相互変換が難しいのはなぜですか」 蓮沼さん「意図的にやってます。スペックリードのステファンが、Dateをぶっころすという気持ちでやってます」 橋本さん「確かに殺意を感じます」 #jjug_ccc
— きしだﬗ (@kis) 2013, 11月 9
155 Concurrency Updates
JavaSE5から導入された並行処理用のAPI(java.util.concurrent)の更新。要点は次の三つ。
- スケーラブルに更新可能な変数(Adder(DoubleAdder、LongAdder等)、Accumulator(DoubleAccumulator、LongAccumulator等))の追加
- ConcurrentHashMap に対するキャッシュ指向の機能拡張(メモリ消費削減、シーケンシャル・並行処理のサポート)
- ForkJoinPool の改良(パフォーマンス改善など)
177 Optimize java.text.DecimalFormat.format
DemicalFormatの最適化。ゴールは2倍の速度改善らしい(micro-benchmark)。
178 Statically-Linked JNI Libraries
statically linked native librariesのサポート(JNI)
180 Handle Frequent HashMap Collisions with Balanced Trees
java.util.HashMap のパフォーマンス改善。バランス木による頻繁な HashMap 衝突のハンドリングによって改善を図る。
185 JAXP 1.5: Restrict Fetching of External Resources
JAXP 1.5の更新。メインは外部リソース取得の制限。
core/lang 関連の JEP
core/sec 関連のJEP
セキュリティ関する以下の機能がサポートされました。新たにサポートされた暗号化方式は何かを知りたい場合は、JEP 115: AEAD CipherSuites を確認しましょう。
- 113 MS-SFU Kerberos 5 Extensions
- 114 TLS Server Name Indication (SNI) Extension
- 115 AEAD CipherSuites
- 121 Stronger Algorithms for Password-Based Encryption
- 123 Configurable Secure Random-Number Generation
- 124 Enhance the Certificate Revocation-Checking API
- 129 NSA Suite B Cryptographic Algorithms
- 130 SHA-224 Message Digests
- 131 PKCS#11 Crypto Provider for 64-bit Windows
- 140 Limited doPrivileged
- 166 Overhaul JKS-JCEKS-PKCS12 Keystores
まとめ
以上、JDK 8 新機能のダイジェストでした!いかがでしたでしょうか、意外にこんな機能も追加されていたんだなーとか何かの気付きになれたら幸いです。
個人的には「 JEP 122: Remove the Permanent Generation 」と「 JEP 155: Concurrency Updates 」はその内もっと掘り下げていくことになりそうです。
Groovy で JVM ログ解析 (ClassHistogram編) #gadvent #java
Groovy で 面倒な JVM ログを解析する (ClassHistogram)
前置き
この記事はG*(Groovy, Grails ..) Advent Calendar 2013 - Qiita [キータ]の10日目です。
前日は研究室の先輩 @tetsurokitaharaさんのGracenote with Groovy でした。
JVM(hotspot) は好きですが Java は余り書かず、作りこむなら Scala、速度が欲しいなら C、ログの解析なら awk/sed、Ruby 等で済ます日々ですが、Java のログなので Groovy でさっと処理してしまおうと考え取り組んでみました。どうせなので groovysh で扱える仮想配列として出力し、後々好きな様に加工できるようにしました。
この題目は7日目の@nobusueさんのGroovyでGCログ解析と多少被ってますが、別のログが対象ということで許してください><
JVM ログで出力される ClassHistogram とは
一般的には OutOfMemoryError が発生した場合などで利用するクラス統計情報で、現在実行中の Java プロセスに存在する各クラスのインスタンス数、バイト数が含まれています。
JVM 起動オプションに -XX:+PrintClassHistogram を指定し、SIGQUIT (kill -3
num #instances #bytes class name -------------------------------------- 1: 24868 3398080 <no name> 2: 24868 2598976 <methodKlass> 3: 2813 2432352 [I 4: 1970 2415680 <constantPoolKlass> 5: 1970 1873040 <instanceKlassKlass> 6: 1873 1661760 <constantPoolCacheKlass> (中略) 1105: 1 16 sun.reflect.GeneratedMethodAccessor3 1106: 1 16 sun.awt.X11.XProtocol$1 Total 172945 22390368
これを間隔を置いて複数回出力させれば、各クラスが時系列にそってどのように増減しているかが調べることが可能です。解りやすい例だと、あるクラスのバイト数が常に増大していれば、明らかにそのオブジェクト(を生成している部分)でメモリリークを起こしてることが判明します。
このログで面倒なのは、複数クラスローダが存在する場合です。クラスローダAとクラスローダBで同じクラスがロードされていると、それは別物扱いなので同じクラス名の情報が重複して出力されます。その上このログからはクラスローダ情報はばっさり捨てられてるので、今回は同じものとして加算しています。
ログから各クラスの時系列ごとの統計情報を解析する
というわけで仮想配列を出力するコードです。実は何気に殆ど初 Groovy だったりするので所々酷いところがあると思います。。
一枚目の gist のコードを保存し、二枚目の gist の通り groovysh で呼び出して仮想配列として出力できます。その後は、例えば以下のようにすればクラス一覧を出力しつつ、各自系列ごとのランク、インスタンス、バイト数情報が引き出せます。
import static ClassHistrogram.* ch = calc("*log") ch.each{ klass, timeline -> println "{${klass}:" timeline.each{ time, value -> println " {${time}:{rank:${value[0]},instance:${value[1]},byte:${value[2]}}}" } println "}" } ---出力例--- {sun.java2d.pipe.LoopPipe: {0:{rank:1040,instance:1,byte:16}} {1:{rank:1057,instance:1,byte:16}} {2:{rank:1071,instance:1,byte:16}} {3:{rank:1079,instance:1,byte:16}} {4:{rank:1079,instance:1,byte:16}} {5:{rank:1081,instance:1,byte:16}} {6:{rank:1082,instance:1,byte:16}} {7:{rank:1079,instance:1,byte:16}} {8:{rank:1078,instance:1,byte:16}} {9:{rank:1078,instance:1,byte:16}} {10:{rank:1078,instance:1,byte:16}} } ---
このように LTSV 形式に出力するのもよし、CSV ファイル形式に出力や特定クラスの情報だけ引っこ抜くのも良しで、加工しやすいのではないかと思います。
以上です、次は https://twitter.com/setoazusa:title@setoazusa さんの番です!
「Java解析ツールバトル」に登壇しました & HeapStats の動かし方について #jjug
JJUG ナイト・セミナー 「Java解析ツールバトル」に HeapStats 陣営でコミッターとして参加してきました!
今回のJJUGでは、自分達の JVM に深く密着してパフォーマンスに心血を注いでいる HeapStats、最近 OSS 化された Java だけでなく SQL 実行計画まで総合的に診断する EndoSnipe、昨日の OracleJDK 7u40のリリースで公式に JDK に付属された Java Flight Recorder と twitter4j の開発者であるモデレータが過去に開発された 侍 の四三つ巴でディスカッションを行いました。
数々のプロジェクトやサービスで利用されている Java だからこそ、障害や性能問題に悩まされる人の数も多いと思われます。障害解析の手助けになるツール、それも今回参加した3つの内2つは日本人によって開発されていることもあり、かなりの盛況ぶりだったと思います:)
自分達のHeapStatsの発表資料はSlideshareで公開しています。興味のある方はぜひご覧ください。
ディスカッションでは時間の関係上、駆け足での説明だったため、「HeapStats はどうやって動かすの?」という最初の一歩をもう少し詳しく補足していきたいと思います。
HeapStatsのインストール
HeapStats インストール条件
性能に心血を注いだ結果として、JVMに手を突っ込み、一部のコードをアセンブラ化している等が原因で、HeapStatsを動かすためには以下の環境である必要があります
HeapStats インストール&動作方法
HeapStats をインストールするためにはバイナリからインストールする方法と、ソースからビルドする方法があります。今回はバイナリからインストールする方法を紹介します。
- バイナリをここから取得してインストールする。
- $ rpm -ivh heapstats-agent-[version].[CPU命令セット].[Linux dist].[アーキテクチャ].rpm
- CPU命令セットの部分は解らない場合は何も記載されていない物を利用して下さい。
- 利用している環境がサポートしているCPU命令セットを確認したい場合、"/proc/cpuinfo" の "flags" を見ることで確認ができます。
- avxが書かれている場合はavxを、avxはないがsse4が書かれている場合はsse4を、それ以外は無印を利用するのがお勧めです。
- Linux dist の fc は Fedora, el は RedHat Enterprise Linux を指します。
- 監視させたい Java プログラムの起動オプションに以下を追加して実行する。
- 実行ディレクトリに heapstats.csv, heapstats.dat が出力される。
- analyzer を利用して可視化が行えます。
- 出力フォルダは設定ファイル /etc/heapstats/heapstats.conf (デフォルト) を編集することで変更できます。
以上で HeapStats は実行している Java プロセスを常時監視しつつ各種情報を取得し、故障発生時には解析に必要な情報を根こそぎ取得するようになります!