Java EEアプリケーションサーバの開発現場で見た Java SEの実際
2015/04/08 日本電気株式会社
翁 信之介
目次
▌ はじめに
▌ Java SEバージョンアップの経験談
▌ Java SE 8 Metaspace領域の解析
▌ NECの取組み
▌ おわりに
本資料で使用するシステム名、製品名は、それぞれ各社の商標、または登録商標です。
なお、本文中ではTM、®、© マークを省略している箇所があります。
はじめに
Java EEの下層に位置するJava SEはOSと同様に品質が重要
▌ Java EEアプリケーションサーバにとってJava SEは肝であり、その品質がシステム全体に与え る影響は大きい
機能、性能、互換性、障害解析ツール、セキュリティ問題、etc.OS
Java SE Java EE
Servlet / JSP EJB
Java VM
RMI-IIOP JNDI JAXP JDBC
JFS JAX-RS
JSON JTA JMS
JPA Batch
Solaris Linux Windows HP-UX
Driver CORBA Directory SV XML Parser
JMX Agent
StAX JAAS JAX-WS JAXB JAF SAAJ
▌ お客様からはOSSのアプリケーションサーバにはない、商用ベンダーならではの厳格な実装品 質と手厚いサポートの期待あり
旧互換性の維持
• 下層SWの振る舞い変更をAPサーバで回避
ex.) Java SEの仕様変更
• 異常系動作の結果やメッセージの維持
• 永年サポート (EJB 1.1 Container on JDK 1.2.2 ;-)
決して許されないデグレード• 動作不正、性能劣化
• Java EE仕様が重厚になっても全パス試験に よる保証は、お客様にとって当たり前品質
セキュリティ脆弱性問題の迅速な対応• 過去の製品に同梱した古いOSSに含まれた セキュリティ問題に対するパッチ提供
ex.) Struts 1、OpenSSL 0.9.x
商用ベンダーに求められる品質
特性 副特性
機能適合性 機能完全性、機能正確性、機能適切性 性能効率性 時間効率性、資源効率性、容量満足性 互換性 共存性、相互運用性
使用性 適切度認識性、習得性、運用操作性、ユーザエラー防 止性、ユーザインタフェース快美性、アクセシビリティ 信頼性 成熟性、可用性、障害許容性(耐故障性)、回復性
セキュリティ 機密性、インテグリティ、否認防止性、責任追跡性、真 正性
保守性 モジュール性、再利用性、解析性、修正性、試験性 移植性 適応性、設置性、置換性
ISO/IEC 25010 システム及びソフトウェア製品の品質要求及び評価(SQuaRE)
− システム及びソフトウェア品質モデル
商用ベンダーの悩み
ログ解析や運用状況のヒアリング等からの事象の分析 ソースコード調査
リリースメモ、JavaDocやマニュアルの確認 バグデータベースの検索
Webに公開されている一般情報 実機での再現検証 ・・・
お客様は即時回答を切望 動作不正
問合せ受付
製品不具合、業務アプリケーションのバグ 連携製品の問題、操作ミス ・・・
迅速、正確、丁寧な対応が顧客満足度に直結
お客様から見るとJava EEの下位層(Java SE、OS)も含めて「アプリケーションサーバ」
Java SE 8 サポートを急ぐ理由
▌ 前回のJava SE 6 EOLでは過去に例をみない大きな話題へと発展
▌
お客様のセキュリティ問題に対する関心が急激に高くなったことが背景にありJava SEにセキュリティ問題がまだ潜在するにも
関わらずEOLが目前行政からの注意喚起を基点に多くの業種で
Java SEのバージョンアップ要求が拡散。メディアでも大きく取り上げられ、短期間に 問合せが殺到
Java SE 5以前で稼働中のシステムにも
最新化検討へ波及。ますます大きな騒ぎに 製品供給者としての社会的責任を果たすべく、迅速な最新 Java SE 対応を推進
内閣官房情報セキュリティセンター (2014/7/17)
Java SE 6のサポート有効期間の満了に係る対応について(注意喚起) 一部抜粋 http://www.nisc.go.jp/active/general/pdf/javasupport_press_120717.pdf
直前に迫るJava SE 7 EOLについては
2015/3/3 内閣官房情報セキュリティセンター ウェブサイト等の利用者に使用を求めているソフトウェアのサポート終了に伴う対応 について(注意喚起)
http://www.nisc.go.jp/active/general/pdf/soft_150303.pdf
Java SE バージョンアップの経験談
▌ Java EEアプリケーションサーバ開発時の直面した、様々なアクシデントをご紹介
開発現場で遭遇した事象
Java SE 8で新規に発生
•Lambdaを使用したアプリケーションが配備できない
•wsimportコマンド実行時にエラーが発生
メジャーバージョンアップで動作が変更
•クラスローダーでデッドロック
新しいUpdateバージョンで動作が変更
•カスタムORBの初期化に失敗
•ロガーのインスタンスがすり替わる
•java.util.Logger#getLogger()でnullを返却
•添付ファイル付きSOAPメッセージで例外
過去から長期にわたって不具合が修正されず
•AWTでjava.lang.UnsatisfiedLinkErrorが発生
1. Lambdaを使用したアプリケーションが配備できない
▌ 事象: Lambdaを使用したアプリケーションを配備すると例外が発生
2015-03-19 19:20:43,025 ERROR DEPLOY - Exception while visiting sample/HelloBean.class of size 1468 [deployment-jar-scanner]
java.lang.ArrayIndexOutOfBoundsException: 52264
▌ 原因: 配備時にバイトコード操作ライブラリ
「ASM」がLambdaを使用したクラスファイルの 解析に失敗するため
Lambdaではクラスファイルのコンスタントプール
にJava SE 7で拡張された InvokeDynamic、MethodHandle、MethodTypeが出現
コンスタントプールの各エントリについて、タグバ イトで型を読み、その先にある型ごとに既定され た長さのデータを読むが、未定義な型によりデー タを誤って読んでしまう03 00 00 00 0A
タグバイト データ
Integer(0x03) の 42(0x0000000A)
12 00 00 00 26
タグバイト データ
?
Java SE 8で新規に発生
▼コンスタントプールの型
1. Lambdaを使用したアプリケーションが配備できない (cont.)
▌ 対処: ASMのバージョンアップ
クラスファイルの拡張自体はJava SE 7で行なわれたが、Javaプログラムから生成されることはなかったため顕在化 しなかったと考えられる。
別のバイトコード操作ライブラリを利用したアプリケーション も注意が必要 (右表)▼Apache Commons BCEL 5.2の実行結果
18(InvokeDynamic)が不正なバイトタグとして例外が発生している。
ライブラリ Java SE 8対応 バージョン
OW2 ASM 5.0
Javassist
(JBossのサブプロジェクト)
3.19.0-GA Apache Commons BCEL 6.0 RC
Exception in thread "main" org.apache.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 18
at org.apache.bcel.classfile.Constant.readConstant(Constant.java:146) at org.apache.bcel.classfile.ConstantPool.<init>(ConstantPool.java:67)
at org.apache.bcel.classfile.ClassParser.readConstantPool(ClassParser.java:222) at org.apache.bcel.classfile.ClassParser.parse(ClassParser.java:136)
at Main.main(Main.java:6)
言語仕様が拡張されると思いもよらぬところで非互換が発生する
▼OSSのバイトコード操作ライブラリ
2. wsimportコマンド実行時にエラーが発生
▌ 事象: Java SE 8でJDK付属のwsimportコマンドを実行した際に、java.lang.AssertionErrorエ ラーが発生
このコマンドの延長で実行するJAXBのバインディングコンパイラ(xjc)において外部スキーマ(インク ルード/インポートしたxsdファイル)を参照するが、そこで処理に失敗▌ 原因
org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory#newSchema(URL)でxsdファイル へのアクセスに失敗
Java SE 8に同梱されているJAXPのバージョンが1.5に更新。 JAXP1.5では外部スキーマ参照時に セキュリティチェックが行われるように変更され、さらに、デフォルト設定では外部スキーマへのアクセ スが不可になっていた。Java SE 8で新規に発生
(※)wsimportとは、WSDLファイルからWebサービスの動作に必要なクラスを生成するためのツール
2. wsimportコマンド実行時にエラーが発生 (cont.)
▌ 対処
一般的には下記のシステムプロパティを設定することで回避可能 (GlassFish 4.1では、domain.xmlにデフォルトで設定済)
XMLSchemaFactoryを使用してXML解析処理を実装している場合、setProperty()メソッドでアクセス 権限を付与することでも対処可能javax.xml.accessExternalSchema=all
file、http、allなど、アクセス権を与えるプロトコルを指定
String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"
//JAXPのXMLSchemaFactoryを取得
SchemaFactory sf = XmlFactory.createSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI, false);
//外部スキーマへのアクセス権限を付与
sf.setProperty(ACCESS_EXTERNAL_SCHEMA,"all");
Schema schema = sf.newSchema(source);
2. wsimportコマンド実行時にエラーが発生 (cont.)
▌ その他
XMLSchemaFactoryクラスを使用しており、かつ外部スキーマ参照があるXML解析を行っていると同 様の問題が発生する可能性あり
JAXPの仕様変更のようにデフォルトの挙動が変わることもあるので、バージョンアップ時の更新内容 をキャッチアップすることは重要3. クラスローダでデッドロック
▌ 事象: アプリケーション実行中に不定期に デッドロックが発生
▌ 原因: 階層構造のクラスローダで逆方向の ロックが発生したため
Java SE 6までは、階層構造のクラスローダで 子から親の順番でロックするのが鉄則Java stack information for the threads listed above:
===================================================
"Thread-0":
at java.lang.ClassLoader.loadClass(ClassLoader.java:404) - waiting to lock <0x099778f8> (a ParentClassLoader) at java.lang.ClassLoader.loadClass(ClassLoader.java:411) - locked <0x0998b920> (a ChildClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at sample.Main$Loader.run(Main.java:61)
"main":
at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:348)
at sun.security.provider.PolicyFile.addPermissions (PolicyFile.java:1357)
(中略)
at java.lang.ClassLoader.loadClass(ClassLoader.java:411) - locked <0x099778f8> (a ParentClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
親クラスローダ 子クラスローダ
main
Thread-0
コンテキストクラス ローダに設定
ロック
ロック待ち
ロック ロック待ち
1 2
1
2
3
4
3
4
a. 親クラスローダでクラスロード中にパーミッション チェックを実行
(例えばaccessClassInPackageで権限が必要な“sun.”から始 まるパッケージのクラスのロード時)
b. sun.security.provider.PolicyFile#addPermissions()で コンテキストクラスローダを用いてクラスロード。し かし、コンテキストクラスローダは子クラスローダで あり、逆順のロックでデッドロックに至る
▼動作例
▼動作例におけるコールスタック
メジャーバージョンアップで動作が変更
3. クラスローダでデッドロック (cont.)
▌ 対処: クラスローダのデッドロックが解消された Java SE 7以降を使用
▌ 原因: 自作クラスローダの処置が未実施
▌ 対処: パラレル対応クラスローダに修正
クラスロードをスレッドセーフにすることも必要だが、クラスローダのクラスの静的初期化子(staticイ ニシャライザ)でjava.lang.ClassLoader#registerAsParallelCapable()メソッドを呼び出すことが重要public class MyClassLoader extedns ClassLoader { static {
ClassLoader.registerAsParallelCapable();
} ...
http://www.oracle.com/technetwork/jp/articles/java/classloaderproposal-428895-ja.html
それでもまだ問題は解消しない・・・
▌ 事象: Java SE 7u55にアップデートするとカスタムORBを利用したアプリケーションでの org.omg.ORB#init()メソッド呼び出しに失敗する
▌ 原因: システムプロパティ
「org.omg.CORBA.ORBSingletonClass」で 指定したORBSingletonクラスをロードする クラスローダの変更
7u51以前: スレッドにコンテキストクラスローダが 設定されていれば「コンテキストクラスローダ」。そうでなければ「システムクラスローダ」
7u55: システムクラスローダのみ
8u5、6u75、5u65も同様に動作変更クラスローダ階層(一部省略)
4. カスタムORBの初期化に失敗
Exception in thread "main" org.omg.CORBA.INITIALIZE: can't instantiate default ORB implementation jp.co.nec.orb.OSPORBSingleton vmcid: 0x0 minor code: 0 completed: No
at org.omg.CORBA.ORB.create_impl_with_systemclassloader(ORB.java:309) at org.omg.CORBA.ORB.init(ORB.java:294)
システムクラスローダ
APサーバのライブラリ用 クラスローダ
アプリケーションクラスローダ アプリケーションクラスローダ
アプリケーションクラスローダ ORBSingleton
新しいUpdateバージョンで動作が変更
ORB#init()実行時に コンテキストクラスロー ダへ設定したライブラ リ用クラスローダに ORBSingletonクラス をロードさせていた
4. カスタムORBの初期化に失敗 (cont.)
▌ リリースノートで示された対処:
カスタムORBのライブラリJarをシステムクラスローダでロードするようにライブラリ配置を変更⇒ 納得いかずJava Bug Databaseに報告
バグとして認められ、製品としては修正された Updateバージョンの使用を告知• 元に戻されたバージョン: 8u20、7u65、6u85、5u75
• Java SE 9ではこの動作が正式なものになる模様 →
システムクラスローダ
APサーバのライブラリ用 クラスローダ
アプリケーションクラスローダ アプリケーションクラスローダ
アプリケーションクラスローダ ORBSingleton
ただし Java SE 9は
Won't Fix
バグとして修正
JDK-8042789
5. ロガーのインスタンスがすり替わる
▌ 事象: Java SEを7u21以前から7u25に更新すると、あるタイミングでLogger#getLogger() 呼び出しで取得したLoggerが別インスタンスを返却するようになった。これにより、以前のイン スタンスで設定したログレベルなどの変更が無効になる。
▌ 原因: セキュリティマネージャー有効時、sun.awt.AppContextクラスのロード前後でロガー名を 管理する空間が別になったため
Appletの独立性を高める変更に影響sun.awt.AppContextクラスはGUIを使用しなくとも、ImageIOなどを使用するとロードされる
Logger.getLogger("foo") -> java.util.logging.Logger@148662
Logger.getLogger("foo") -> java.util.logging.Logger@1829e6f
Logger before = Logger.getLogger("test");
Class.forName("sun.awt.AppContext");
Logger after = Logger.getLogger("test");
System.out.printf("before: %s, after: %s", before, after);
新しいUpdateバージョンで動作が変更
▼アプリケーションサーバ起動中
▼起動後
JDK-8024827
5. ロガーのインスタンスがすり替わる (cont.)
▌ 対処: 起動処理の早い段階で sun.awt.AppContext クラスロード処理を追加
他にも、システムプロパティ「javaplugin.version」あるいは「javawebstart.version」が設定されていれ ば発生しないことが判明。ただし、明示的に設定することに対する影響度は不明
または、Java SE 7u60で改修されたため、このUpdateバージョン以降を使用する6. java.util.Logger#getLogger()でnullを返却
▌ 事象: Java SE 6u18でLogger#getLogger()メソッドが稀にnullを返す
▌ 原因:
Logger#getLogger()メソッドでは、指定されたロガー名に対応するLoggerが存在しない場合、次の 順序でLoggerを取得して呼び出し元に返却① Logger生成
② Loggerを弱参照マップに登録
③ 弱参照マップ登録したLoggerを取得
②と③の間にGCが発生すると、登録したLoggerがクリアされ、結果としてnullが返る◇ 参考 「JavaDoc (java.util.Logger#getLogger()メソッド)」
新しいUpdateバージョンで動作が変更
6. java.util.Logger#getLogger()でnullを返却 (cont.)
private Hashtable<String,WeakReference<Logger>>
loggers = new Hashtable<String,WeakReference<Logger>>();
Logger demandLogger(String name) { Logger result = getLogger(name);
if (result == null) {
result = new Logger(name, null);
addLogger(result);
result = getLogger(name);
}
return result;
}
private Hashtable<String,WeakReference<Logger>>
loggers = new Hashtable<String,WeakReference<Logger>>();
Logger demandLogger(String name) { Logger result = getLogger(name);
if (result == null) {
addLogger(new Logger(name, null));
result = getLogger(name);
}
return result;
}
JVMによるコードの最適化 (イメージ)
メモリリーク防止のためJava SE 6u18 から弱参照に変更
addLogger()終了後からgetLogger() までの間にGCが起こると、addLogger() で Hashtableに弱参照で登録した
Loggerオブジェクトが消える
無駄な代入を省く最適化によりインスタ ンス化したLoggerがresult変数に参照さ れなくなるため、LoggerがGCによるクリ アの対象になる
▼Java SE 6u18のgetLogger()内部処理
6. java.util.Logger#getLogger()でnullを返却 (cont.)
▌ 対処: Java SE 6u39以降を使う
Java SE 6u39以降ではLoggerオブジェクトが取得できるまで、ロガーの作成と取得を繰り返すように Logger#getLogger()メソッドの内部実装が変更されているLogger demandLogger(String name, String resourceBundleName) { Logger result = getLogger(name);
if (result == null) {
Logger newLogger = new Logger(name, resourceBundleName);
do {
if (addLogger(newLogger)) { return newLogger;
}
result = getLogger(name);
} while (result == null);
}
return result;
}
Loggerオブジェクトを取得 できるまで繰り返す
▼Java SE 6u39の内部処理
7. 添付ファイル付きSOAPメッセージで例外
▌ 事象: Webサービス(JAX-WS)で添付ファイル付きSOAPメッセージを処理する際に例外発生
Metro 2.3.0バンドル版のJAX-WSが該当
Java SEに付属するJAX-WSでは発生しない▌ 原因: Java SE 7u55でjavax.activation.CommandMap#getDefaultCommandMap()メソッド に修正がされ、添付ファイル処理用に初期化を行ったCommandMapオブジェクトを取得でき なくなっていた
初期化処理で添付ファイルを処理するためのハンドラを登録
動作時にハンドラが登録されていないCommandMapを取得し、添付ファイルの処理に失敗新しいUpdateバージョンで動作が変更
7. 添付ファイル付きSOAPメッセージで例外 (cont.)
public static CommandMap getDefaultCommandMap(){
if (defaultCommandMap == null) {
defaultCommandMap = new MailcapCommandMap();
}
return defaultCommandMap;
}
public static synchronized CommandMap getDefaultCommandMap(){
if (defaultCommandMap != null) { return defaultCommandMap;
}
ClassLoader tccl = SecuritySupport.getContextClassLoader();
CommandMap def = (CommandMap)map.get(tccl);
if (def == null) {
def = new MailcapCommandMap();
map.put(tccl, def);
}
return def;
}
▼Java SE 7u51以前
▼Java SE 7u55以降
クラスローダごとにCommandMap を管理するよう変更。
Java SE 7u51までは
defaultCommandMapだけを保持 しており、初期化は初回取得時に 一度だけで良かった。
7. 添付ファイル付きSOAPメッセージで例外 (cont.)
▌ 対処
Metro 2.3.1バンドル版のJAX-WSを使用• CommandMapの初期化タイミングを変更
(スタティックイニシャライザ → コンストラクタ)
• 初期化済みか否かのチェックを追加し、未初期化の場合のみ初期化処理を実行
▌ その他
CommandMapを直接使用していなくても、JAX-WSのようにライブラリ側で対応できているか注意が 必要
MetroのWebサービスセキュリティ機能でも同様のロジックがあり、暗号化処理がスキップされる問題あ り(Metro 2.3.1は未修整)• レスポンスの暗号化結果が空になるだけでログに一切エラーが出ないため、原因の特定が難航
• 暗号化関連の処理を辿って延々とデバッグすることに (;_;)
Bug_id:8043129
8. AWTでjava.lang.UnsatisfiedLinkErrorが発生
▌ 事象: Linux x86環境において、 JAWT(AWTのJNI用ライブラリ)にリンクされた自製ライブラ リの呼び出しで、libmawt.soへのjava.lang.UnsatisfiedLinkErrorエラーが発生
▌ 原因: AWT関連の仕様変更の影響により、動作プロセスのカレントディレクトリからの相対パス でlibmawt.soが検索されるため
背景:Java SE 5からAWTのライブラリがXToolkitとMToolkit(motifベース)に分離。どちらを利用するかは 実行時に決まり、AWTライブラリがロードされたときにのみ呼び出される。しかし、JAWT利用時はこの ライブラリ選択処理がスキップされて参照パスが不正になり、上記の問題が発生
java.lang.UnsatisfiedLinkError: Can't load library: /opt/curr/lib/headless/libmawt.so
過去から長期にわたって不具合が修正されず
$JAVA_HOME/jre/lib/amd64/headless/libmawt.soを参照することを期待していたが、
カレントディレクトリ(/opt/curr)からの相対パス(./lib/headless/libmawt.so)で検索が行われている
8. AWTでjava.lang.UnsatisfiedLinkErrorが発生 (cont.)
▌ 発生条件: JAWTにリンクされた自製ライブラリをロードする
GUIを表示しないアプリケーションでも、例えば、javax.imageio.ImageIO#read()メソッドを呼び出した 延長でjava.awt.image.ColorModelクラスが利用され、本件と同じ事象が発生する。▌ 対処: 不具合に抵触するUpdateバージョンを使用する場合は、動作プロセスのカレントディレ クトリからAWTライブラリを検索できるようにシンボリックリンクを設定する
本件はJava SE 5~8で発生するが、現在公開されている最新版では改修済み• 改修されたバージョン: 8u5、7u55、6u75、5u65
Java SE 8 非互換項目の一覧 (1/2)
項番 非互換項目 非互換項目の種類
1 DatagramPacketコンストラクタの動作が変更
コンパイルができない 2 Java言語仕様の15.21項の内容を正しく適用
3 ジェネリクスが指定された型とRAW型における型チェックが変更 4 ある条件でProxyクラスがpublicからnon-publicに変更
実行時に例外が発生 5 Proxyクラスのコンストラクタの挙動が変更
6 CollectionインタフェースのremoveAllメソッドとretailAllメソッドの動作を変更 7 ソケットにポート番号を割り当てる範囲に関して挙動が変更
8 キーストロークがAWTKeyStrokeであるかのチェック方法を追加 9 JDK 8の制限付きパッケージにcom.sun.media.soundを追加
10 JDK 8の制限付きパッケージに com.sun.corba.se関連のパッケージを追加 11 DateFormatとSimpleDateFormatの出力形式が変更
実行結果が異なる 12 BigDecimalクラスのstripTrailingZerosメソッドの戻り値が変更
13 コマンドラインのPermSizeとMaxPermSizeフラグが削除
14 NumberFormatクラスとDecimalFormatクラスの丸め動作を変更
Java SE 8 非互換項目の一覧 (2/2)
項番 非互換項目 非互換項目の種類
15 合成ブリッジメソッドを生成するときの挙動が変更
実行結果が異なる 16 Invokespecial命令の検証が変更
17 JVMでのACC_SUPERフラグの扱いが変更
18 TypeVariableクラスのgetUpperBoundメソッドの戻り値が変更 19 WWW-Authenticateレスポンス・ヘッダ内の値が一部変更
20 LocaleServiceProviderの実装が変更
21 JSSEプロバイダにおける新しいシステムプロパティが追加 22 Windowsにおけるuser.homeディレクトリの判別手順を変更 23 インタフェースの管理機能公開の条件を変更
24 “1.4.1”,”1.4.2”,”jsr14”がjavacで認識されないように変更 25 内部クラス用のコンストラクタを生成するときの挙動が変更
26 SecurityManager存在時におけるJAXPのXalan拡張関数が変更 27 JAXP関連のサービスプロバイダに関する挙動が変更
28 javax.xml.streamパッケージの一部クラスのnewFactoryメソッドの動作が変更
JDK 8の互換性ガイド http://www.oracle.com/technetwork/jp/java/javase/overview/8-compatibility-guide-2156366-ja.html
Java SE 8適用の利点
▌ 様々なことがあったが、Java EEアプリケーションサーバにJava SE 7と8を適用した場合に 性能面で効果を確認
▌ アプリケーション実行性能は9%向上、アプリケーションサーバ起動時間は27%短縮
0.00 0.20 0.40 0.60 0.80 1.00 1.20
Java SE 7u76 Java SE 8u40
Servletの スループット
EJBの スループット
アプリケーションサーバの 起動時間
【測定環境】
•CPU: Intel(R) Xeon(R) CPU E31270 @ 3.40GHz (4コア)
•RAM: 16GB
•OS : Red Hat Enterprise Linux 6.5
•AP Server: WebOTX Application Server Express V9.3
Java SE 8 Metaspace領域の解析
気になるMetaspaceの使用状況
Java SE 8でパーマネント領域からMetaspace領域に代わり OutOfMemoryErrorの発生頻度は減少
チューニング不要と思いきや、メモリリークしていたり、大量のメモリを使用する場合は ネイティブ領域のメモリ空き容量が減少し、他のプロセスに影響を及ぼすことがある
メモリ関係でトラブルになった場合にMetaspaceの解析ができるように なっておくことはJava開発者にとっては有益
Metaspaceの解析方法をご紹介!
JEP 122:Remove the Permanent Generation
▌
Java SE 8 からクラス情報は Native memory に移動!
Java Heap の世代は New と Old のみになり、Permanent は廃止!Eden
From To
Tenure Survivor
Young Generation Old Generation
Thread
stack C Heap
Permanent Generation Permanent
Eden
From To
Tenure
Survivor Thread
stack C Heap
Metaspace
< Java SE 7 以前>
< Java SE 8 以降>
Java Heap
Java Heap
Native Memory
Native Memory
Code Cache
Code Cache
Metaspace領域の構造
▌ Metaspace領域は、Metachunkリストの集合からなる仮想的な領域
ClassLoader単位に“a Metascpace”は作成される
“a Metaspace”は Metachunkのリストから構成される仮想的な領域Metachunkは Native memory 内の Virtual space 内に確保される
Virtual space には複数のMetachunkが格納される
Metachunkリストは複数の Virtual space を跨ぐことがあるJava Heap
Metachunk -Boot 1
Virtual space 1 Virtual space 2
Metachunk -A1
Metachunk -A2
ClassLoader - A Bootstrap
ClassLoader Young / Old
Metaspace
Metachunk -Boot 2 Metachunk
-A3
Block Block Block Block Block Block Block Block Block Block Block Block Block
Block Block
Native Memory “a Metaspace”
Reserved space
Reserved Size
Committed Size
【Metaspace領域の各サイズ】
Reserved: Virtual space容量の合計 Committed: Metachunk容量の合計
(Full GCによって空いた領域も含む) Capacity: Metachunk使用量の合計
Metaspaceの拡張とチューニング・パラメータの関係
① -XX:MaxMetaspaceSize=<NNN> (default:無制限)
②
-XX:MetaspaceSize=<NNN>
(default:12MB ~ 22MB程度)③ -XX:MinMetaspaceFreeRatio=<NNN>(default:40)
④ -XX:MaxMetaspaceFreeRatio=<NNN>(default:70)
High water mark
①
②
③
④
FullGC Used
MaxMetaspaceSize
Committed
0
+ Metachunk Size
FullGC + Allowed expand
Size
NEXT - High water mark
Request Full GC
Request Full GC
Compute Next HWM
Compute Next HWM
Full GC 発生後の High water mark の拡張と縮小を制御
+ Metachunk Size + Metachunk Size
OutOfMemoryError: Compressed class space
▌ 発生原因:
CompressedClassPointers 機能のための class space の枯渇が原因
▌ 対処:以下の何れかを実施
① Compressed class space のサイズを増やす
-XX:CompressedClassSpaceSize=<NNN>
(サイズは、1048576(1MB) ~ 3221225472(3GB) の間で指定する必要あり。デフォルトは1GB)
② Compressed class space を OFF にする
-XX:-UseCompressedClassPointers
Compressed Class Space Metasapce
Klass Klass Bytecode
Bytecode
※補足:
Metaspace の枯渇を原因とする Full GC を発生させる閾値(HWM)の初期値(-XX:MetaspaceSize)も合わせてチューニングすることをお勧めします。
MetaspaceSize には Compressed Classs space のサイズも含まれます。
Java Heap
※64bit OSの場合のみ
OutOfMemoryError: Metaspace
▌ 発生原因:
メッセージの出力通り、Metaspace の枯渇が原因
▌ 対処:
① Metaspace の最大サイズを指定している場合、その値を大きくする
-XX:MaxMetaspaceSize=<NNN>
(またはオプションによるサイズ指定を止める!)
② Compressed Class Spaceが必要以上に大きい場合、その値を小さくする
-XX:CompressedClassSpaceSize=<NNN>
(サイズは、1048576(1MB) ~ 3221225472(3GB) の間で指定する必要あり。デフォルトは1GB)
③ Java Heap が必要以上に大きい場合、その値を小さくする
-Xmx<NNN> -Xms<NNN>
Compressed Class Space Metasapce
Klass Klass Bytecode
Bytecode
Java Heap
-verbose:gc or -Xloggc のみ設定時のログの読み方
▌ 残念ながら通常のGCログでは
Metaspace の状況は確認できません
Metaspce の状況はまったくログに出力されない
Metaspce が High Water Mark に達したことが原因で発生した Full GC を見分けるための情報 だけは出力される[GC (Allocation Failure) 4059K->1399K(9920K), 0.0010878 secs]
[Full GC (Metadata GC Threshold) 2553K->1062K(9920K), 0.0125266 secs]
[GC (Allocation Failure) 3814K->1199K(9920K), 0.0007221 secs]
Metachunk -Boot 1
Virtual space 1 Virtual space 2
Metachunk-A1 Metachunk-B1 Metachunk-B2
Block Block Block Block Block Block Block Block Block
Block Block
Metaspace
Reserved space
?
-XX:+PrintGCDetails 設定時のログの読み方
▌
Full GC 発生時点での Metaspace の状況だけが確認できる使用量に関しては Metaspace の箇所に限り、変化を表現していない
• Metaspace の Purge 処理は GC ログ出力後に実施される
Compressed Class Space を利用している場合
• 各数値には Compressed Class Space の使用量および容量が含まれている
[GC (Allocation Failure) [DefNew: 2883K->223K(3072K), 0.0009640 secs] 4059K->1399K(9920K), 0.0010878 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Metadata GC Threshold) [Tenured: 1175K->1062K(6848K), 0.0055470 secs] 2553K->1062K(9920K),
[Metaspace: 6552K->6552K(8576K)], 0.0125266 secs]
[Times: user=0.02 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [DefNew: 2752K->136K(3072K), 0.0006252 secs] 3814K->1199K(9920K), 0.0007221 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Metachunk使用量の合計 Virtual space容量の合計
[Metaspace: ① -> ② ( ③ )]
①②: Full GC 時点の Metaspace 全体の used 量
③ : Full GC 時点の Metaspace 全体の Reserved 量
-XX:+PrintHeapAtGC 設定時のログの読み方 (1/2)
▌ Full GC 後の状況ログから
Metaspace の状況変化を確認できる!{Heap before GC invocations=13 (full 2):
def new generation total 3072K, used 1417K [0x04000000, 0x04350000, 0x04350000) eden space 2752K, 43% used [0x04000000, 0x0412a660, 0x042b0000)
from space 320K, 69% used [0x04300000, 0x04337ef0, 0x04350000) to space 320K, 0% used [0x042b0000, 0x042b0000, 0x04300000)
tenured generation total 6848K, used 1175K [0x04350000, 0x04a00000, 0x04a00000) the space 6848K, 17% used [0x04350000, 0x04475e38, 0x04476000, 0x04a00000)
Metaspace used 6552K, capacity 8510K, committed 8536K, reserved 8576K
[Full GC (Metadata GC Threshold) [Tenured: 1175K->1062K(6848K), 0.0055370 secs] 2592K->1062K(9920K),
[Metaspace: 6552K->6552K(8576K)], 0.0102723 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Heap after GC invocations=14 (full 3):
def new generation total 3072K, used 0K [0x04000000, 0x04350000, 0x04350000) eden space 2752K, 0% used [0x04000000, 0x04000000, 0x042b0000)
from space 320K, 0% used [0x04300000, 0x04300000, 0x04350000) to space 320K, 0% used [0x042b0000, 0x042b0000, 0x04300000)
tenured generation total 6848K, used 1062K [0x04350000, 0x04a00000, 0x04a00000) the space 6848K, 15% used [0x04350000, 0x04459af0, 0x04459c00, 0x04a00000)
Metaspace used 817K, capacity 2734K, committed 5464K, reserved 5504K
}
Metachunk容量の合計
Metachunk使用量の合計 Virtual space 容量の合計
Before GCAfter GC
Metachunk -Boot 1
Virtual space 1 Virtual space 2
Metachunk -Boot 2
Block Block Block Block Block Block Block Block
Vacant space 1
Vacant space 3
Reserved space
Reserved Size
Committed Size
Vacant space 2
-XX:+PrintHeapAtGC 設定時のログの読み方 (2/2)
▌
64bit OSの場合、Compressed Class Space の状況も出力される{Heap before GC invocations=3 (full 2):
def new generation total 19712K, used 1189K [0x00000000c0000000, 0x00000000c1560000, 0x00000000d5550000) eden space 17536K, 3% used [0x00000000c0000000, 0x00000000c00abba0, 0x00000000c1120000)
from space 2176K, 23% used [0x00000000c1340000, 0x00000000c13bdc50, 0x00000000c1560000) to space 2176K, 0% used [0x00000000c1120000, 0x00000000c1120000, 0x00000000c1340000)
tenured generation total 43712K, used 1103K [0x00000000d5550000, 0x00000000d8000000, 0x0000000100000000) the space 43712K, 2% used [0x00000000d5550000, 0x00000000d5663d10, 0x00000000d5663e00, 0x00000000d8000000)
Metaspace used 12224K, capacity 12346K, committed 12448K, reserved 1060864K class space used 1947K, capacity 2002K, committed 2048K, reserved 1048576K
[Full GC (Metadata GC Threshold) 1.569: [Tenured: 1103K->1353K(43712K), 0.0083164 secs] 2293K->1353K(63424K),
[Metaspace: 12224K->12224K(1060864K)], 0.0100021 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap after GC invocations=4 (full 3):
def new generation total 19712K, used 0K [0x00000000c0000000, 0x00000000c1560000, 0x00000000d5550000) eden space 17536K, 0% used [0x00000000c0000000, 0x00000000c0000000, 0x00000000c1120000)
from space 2176K, 0% used [0x00000000c1340000, 0x00000000c1340000, 0x00000000c1560000) to space 2176K, 0% used [0x00000000c1120000, 0x00000000c1120000, 0x00000000c1340000)
tenured generation total 43712K, used 1353K [0x00000000d5550000, 0x00000000d8000000, 0x0000000100000000) the space 43712K, 3% used [0x00000000d5550000, 0x00000000d56a2470, 0x00000000d56a2600, 0x00000000d8000000)
Metaspace used 7810K, capacity 7874K, committed 12448K, reserved 1060864K class space used 1096K, capacity 1130K, committed 2048K, reserved 1048576K
}
Metachunk容量の合計 Metachunk使用量の合計
起動時に確保した連続領域サイズ
Before GCAfter GC
Metaspace used ①, capacity ②, committed ③, reserved ④ class space used ⑤, capacity ⑥, committed ⑦, reserved ⑧
Compressed Class Space を除く通常の Metaspace のサイズを確認するには・・・
used ① ー ⑤, capacity ② ー ⑥, committed ③ ー ⑦, reserved ④ ー ⑧
GC ログで Metasapce を確認する場合
▌ 最低でも以下のオプションを一緒に設定することをお勧めします!
-Xloggc:<FILE>
-XX:+PrintHeapAtGC
-XX:+PrintGCDetails
▌ クラスアンロードの詳細も確認したい場合、以下のオプションを上記オプションに追加する 必要があります
-verbose:class
jstat コマンドによる確認方法
▌ 実行例
jstat -gc <Java PID>▌ 出力結果
MC:現在の Metaspace Capacity(KB)
MU:Metaspace Utilization(KB)
CCSC:現在の Compressed Class Space Capacity(KB)
CCSU:Compressed Class Space Used(KB)
S0C S1C OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 2176.0 2176.0 4491.4 26104.0 21449.2 7928.0 3707.8 55 0.219 11 0.174 0.393
Metaspace の情報
▌ 実行例
jstat -gcmetacapacity <Java PID>▌ 出力結果
MCMN:Minimum Metaspace Capacity(KB)
MCMX:Maximum Metaspace Capacity(KB)
MC:Metaspace Capacity(KB)
CCSMN: Minimum Compressed Class Space Capacity(KB)
CCSMX: Maximum Compressed Class Space Capacity(KB)
CCSC:Compressed Class Space Capacity(KB)
MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT 0.0 1069056.0 27336.0 0.0 1048576.0 8136.0 90 17 0.376 0.815
Metaspace の情報
jmap コマンドによる確認方法
▌ 実行例
jmap –J-d64 -clstats <Java PID>▌ 出力結果
※: 上記オプション内の “-J-d64” は、64bit Java VM の場合に限り設定する
Attaching to process ID 296, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.25-b02
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness.liveness analysis may be inaccurate ...
class_loader classes bytes parent_loader alive? type
<bootstrap> 662 1201022 null live <internal>
0x00000000d5577c40 0 0 null dead sun/misc/Launcher$ExtClassLoader@0x0000000011a00448 0x00000000d5916068 474 483159 null dead java/net/URLClassLoader@0x0000000011a000b0
0x00000000d55516b8 25 83246 0x00000000d5577c40 dead sun/misc/Launcher$AppClassLoader@0x0000000011a0a280 total = 4 1161 1767427 N/A alive=1, dead=3 N/A
Metaspace に関連するクラスローダーの情報
jcmd コマンドによる確認方法
▌ 実行例
jcmd <Java PID> PerfCounter.print▌ 出力結果
・・・・ 省略
sun.gc.compressedclassspace.capacity=10690560
sun.gc.compressedclassspace.maxCapacity=1073741824 sun.gc.compressedclassspace.minCapacity=0
sun.gc.compressedclassspace.used=8932912
・・・・ 省略
sun.gc.metaspace.capacity=51060736
sun.gc.metaspace.maxCapacity=1115684864 sun.gc.metaspace.minCapacity=0
sun.gc.metaspace.used=48759312
・・・・ 省略
Compressed Class Space の情報
Metaspace の 情報
▌ 実行例
jcmd <Java PID> GC.class_stats▌ 出力結果
※ アタッチする Java AP は“-XX:+UnlockDiagnosticVMOptions”
を設定して起動する必要があります
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName 1 -1 2954528 496 0 1152 14 121 2904 1120 3648 4768 java.lang.Object
2 -1 2260280 480 0 0 0 0 0 24 576 600 [I
3 -1 998560 480 0 0 0 0 0 24 576 600 [Ljava.lang.Object;
4 -1 433264 480 0 0 0 0 0 24 576 600 [C
5 1 169704 648 0 19304 130 4962 26392 16504 31600 48104 java.lang.Class
・・・・ 省略
1612 1 0 544 0 1376 7 247 1280 880 2544 3424 sun.util.locale.LocaleObjectCache 1613 1 0 496 0 1416 20 737 3736 2240 3672 5912 sun.util.locale.LocaleUtils
7333552 1329504 2736 3031360 17467 1131774 4163760 2822720 6212920 9035640 Total 81.2% 14.7% 0.0% 33.5% - 12.5% 46.1% 31.2% 68.8% 100.0%
Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName
Metaspace に格納されるクラス定義のヒストグラム
Java VisualVM / JConsole による確認方法
▌ 実行例
jvisualvm▌ 出力結果
「監視」タブで Metaspace の状況
(Compressed Class Space を含む)を確認する
Metaspace の情報
▌ 実行例
jconsole▌ 出力結果
「メモリ」タブで Metaspace / Compressed Class Space の状況を確認する
Metaspace の情報
NECの取組み
高性能・高信頼なWeb/APサーバWebOTXがV9.3にバージョンアップ
次世代のコンテナ型仮想化技術「Docker」を先取り!
http://jpn.nec.com/webotx/
2015年5月8日出荷予定
DB クライアント サーバ
Express
ロード バランサ
▌
主な機能強化内容1. 高信頼基盤の性能と可用性を向上
Web / APサーバ間の通信方式を刷新
通信速度を30%以上高速化
Apache HTTP Server 2.4の最新化
WebSocket、メモリ削減と高速化、負荷分散制御、クラ スタ機能、最新SSL/TLSモジュールのバンドルなど
2. 最新のプラットフォームに対応
Java SE 8、Red Hat Enterprise Linux 7
Java SE 8を適用したAPサーバ起動性能は、Java SE 7 比で30%向上
次世代の仮想化技術であるコンテナ型仮想技術
「Docker」に対応
製品に関する詳細は以下のURLをご参照ください
Webサーバ #1
Webサーバ #2
APサーバ #1
APサーバ #2 Standard
Express Standard
HTTP サーバ アプリケーションサーバ 高度な負荷分散構成
クラスタ運用が可能
クラウド環境への対応を強化! システムの仮想化、集約化での引合い多数!
無料のお試し版としてAWS上でのシステム自動構築サンプルを公開中
AWS
Management Console
AWS
CloudFormation template
AMI EC2
instance
Amazon RDS
WebOTX と一緒に Infrastructure as Code を体験!
http://jpn.nec.com/webotx/download/develop.html#aws
システム設計に対する性能・可用性評価を支援するサービスも開始予定!
進化し続ける「NEC WebOTX」。製品誕生から18年目へ
J2EE 1.2 Webサービス
COBOL EJB 1.1
RMI-IIOP IIOP-SSL EJB 1.0
動的負荷分散 OLTP
CORBA Java対応等
V6 V5
V4 V3
V2 V1
Webサービス
Java EE (Servlet/JSP、EJB、...)
•メインフレームの高信頼性技術に基づくトランザクション管理
•分散オブジェクト (CORBA、COM)
SOA
NGN、ユビキタス
Java SE 6 IPv6
SIP 音声 RFID (EPCIS) ISO/IEC 15408
’98
V10
J2EE 1.4 JDK 5.0 WS-BPEL 2.0 ESB (JBI 1.0) 高速XMLパーサ 統合IDE (Eclipse) J2EE 1.3
CORBA 2.6
.NET Portal
バッチ
クラウド、仮想化
V7
’99 ’00 ’01 ’02 ’03 ’04 ’05 ’06 ’07 ’08 ’09 ’10 ’11 ’12 ’13 ’14
V8
Java EE 5 国際化対応
Java EE 6 Java SE 7, 8
AWS
インメモリデータグリッド
’15~
Java EE 7 準備中
V9