metaspace
DESCRIPTION
JJUG CCC 2014 Spring資料TRANSCRIPT
MetaspaceをOpenJDK実装から見てみよ
う!末永 恭正 @YaSuenag
#ccc_r25
おことわり この資料は 2014/05/17 時点の JDK8u/
jdk8u-dev/hotspot ( changeset 382a82b0a3e7 : jdk8u20-b14 )の内容を基に記載しています。
Oracle と Java は、 Oracle Corporation 及びその子会社、関連会社の米国及びその他の国における登録商標です。文中の社名、商品名等は各社の商標または登録商標である場合があります。
自己紹介 末永 恭正(すえなが やすまさ) @YaSuenag
某 SIer で Java やってるサンデープログラマー
OpenJDK Author ( jdk9 ) IcedTea Committer
HeapStats 作ってますhttp://icedtea.classpath.org/wiki/HeapStats/jp
Metaspaceって、何?
Metaspace
Permanent 世代の代わりに導入されたメタデータ管理の仕組みJEP 122: Remove the Permanent Generation
http://openjdk.java.net/jeps/122
“HotRockit” の一環
Perm 世代をネイティブメモリに追い出す
扱うデータ JVM 実装で MetaspaceObj クラスを継承して
いるものが Metaspace で扱われるClassType
○ 純粋なクラス情報NonClassType
○ ClassType 以外の情報○ シンボル情報やメソッドの最適化に必要な情報など
MetaspaceObj の一覧
Metaspace の種類StandardMetaspaceType 通常の Metaspace
BootMetaspaceType ブートストラップクラスローダ専用
ROMetaspaceType クラス情報ダンプ時に使用される Metaspace( -XX:+DumpSharedSpaces )
ReadWriteMetaspaceType
AnonymousMetaspaceType 匿名クラス専用( InvokeDynamic )
ReflectionMetaspaceType defineClass 専用(リフレクションなどの動的定義クラス)
メモリレイアウト
ClassLoader とVirtualSpace
ClassLoader
仮想メモリ空間VirtualSpaceList
VirtualSpace
VirtualSpace
:VirtualSpace
VirtualSpace
:
ClassLoader
メタデータ用のバラバラの空間を仮想的に 1 つに=Metaspace
VirtualSpaceCompressedClassSpace !?
CompressedClassSpace
圧縮 Oop が利用可能な状況でのみ使用できる、クラス情報に特化したメモリ空間UseCompressedOops が有効であることが必須
○ 最大 Java ヒープサイズが 32GB 以下であること UseCompressedClassPointers でコントロー
ル可能
圧縮 Oop ? LP64 向け HotSpot でのみ利用可能なメモリ
使用量削減の仕組み 64bit ポインタを、ベースアドレスからのオ
フセットを用いてムリヤリ 32bit で表現
どういうこと? LP64 のメモリアライメントは 8byte
ObjectAlignmentInBytes でコントロール○ デフォルト: 8
つまり、下位 3bit はゼロで埋められている3bit 右シフトしても情報欠損が起きない!
Java ヒープや CompressedClassSpace は連続空間そこの上にアロケートされるメモリは、すべて開始
アドレスからのオフセットで表現可能!
プログラム的に表すと…http://www.oracle.com/technetwork/jp/articles/java/compressedoops-427542-ja.html
圧縮 Oop ( narrowOop )は符号なし 32bit で表現されるのでuint_max ( 4GB ) << 3 = 4GB×23 = 32GB
圧縮できる上限
Metaspaceの成長
Metaspace の成長= VirtualSpaceList の成長
=VirtualSpace の追加 成長度合い
チャンクサイズで決まります
Metaspace とチャンクサイズ
チャンクサイズはイジれません…
HotSpot の 1 ワード: LP64 なら 8 バイト、それ以外なら 4 バイト
対数( log )です
ここまでのまとめ Metaspace は 1 クラスローダにつき 1 つ割り
当てられる Metaspace は連続したメモリ空間ではない Metaspace で扱われる情報は大きく 2 種類
クラス情報それ以外
Metaspace には 6 つの種類が存在する LP64 環境で Java ヒープサイズが 32GB 以下
の場合は CompressedClassSpace が作られる
都市伝説(?):MetaspaceではOutOfMemoryErrorは起きない?
Metaspace でもOutOfMemoryError は起きます!
実証コードimport java.nio.file.*;import java.util.jar.*;
public class MetaFlood{
public static void main(String[] args) throws Exception{ Path rtjar_path = FileSystems.getDefault().getPath( System.getProperty("java.home"), "lib", "rt.jar");
try(JarFile rtjar = new JarFile(rtjar_path.toFile())){ rtjar.stream().filter(e -> !e.isDirectory()) .map(e -> e.getName()) .filter(n -> n.endsWith(".class")) .map(n -> n.substring(0, n.length() - 6).replace('/', '.')) .forEach(n -> { System.out.println(n); try{ ClassLoader.getSystemClassLoader().loadClass(n); } catch(ClassNotFoundException ex){} catch(OutOfMemoryError oome){ System.out.println(oome.toString()); throw oome; } }); }
}
}
OOME を起こしてみる
$ /usr/local/jdk1.8.0_05/bin/java -XX:CompressedClassSpaceSize=1m MetaFlood :java.lang.OutOfMemoryError: Compressed class spaceException in thread "main“ :
$ /usr/local/jdk1.8.0_05/bin/java -XX:-UseCompressedClassPointers \ -XX:MaxMetaspaceSize=5m MetaFlood :java.lang.OutOfMemoryError: MetaspaceException in thread "main“ :
その1: CompressedClassSpace 溢れ
その2: Metaspace 溢れ
MetaspaceとGC
Permanent との違い Metaspace が GC されることはありません
クラスローダが破棄されると、関連するMetaspace が削除されます
FullGC 後に Metaspace 容量の調整を行います拡張 or 削減ここでは閾値の変更のみを行い、実際のメモリ伸
縮は行いません
MetaspaceGC
Metaspace 起因の GC が起こるまで
1. Metaspace からメモリを取ろうとするi. 今あるメモリから取ろうとするii. Metaspace を拡張して取ろうとする
2. GC を起こすi. 使われていないクラスローダを GC で回収し、
できるだけ空き Metaspace を増やすii. Metaspace を拡張して取ろうとする
~通常はここまでで Metaspace がとれる~
→OutOfMemoryError
GC ログの例[Full GC (Metadata GC Threshold) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 388K->836K(6656K)] 884K->836K(9216K), [Metaspace: 7292K->7292K(9216K)], 0.0123187 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
[Full GC (Last ditch collection) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 836K->792K(6656K)] 836K->792K(9216K), [Metaspace: 7292K->7292K(9216K)], 0.0134180 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]
※見やすく改行しています
Metaspace が足りなくてGC が発生
GC してもまだ足りないため
最後の悪あがき
「最後の悪あがき」とは? ソフト参照も回収対象にし、できる限りクラス
ローダを回収しようとします
どうチェックする?Metaspace使用量
5 つの手段1. ツール
jstatjcmdjconsoleVisualVM
2. JMX
3. GC ログ4. NMT
5. HeapStats
jstat
-gc などが使えます(単位は全部 KB )MC : Metaspace CapacityMU : Metaspace UsedCCSC: Compressed Class Space Capacity CCSU: Compressed Class Space Used
-gcmetacapacity というオプションもありますメタスペースサイズと GC回数・時間の取得用
$ /usr/local/jdk1.8.0_05/bin/jstat -gc 7831… MC MU CCSC CCSU …… 4864.0 2377.7 512.0 258.0 …
jcmd
PerfCounter.print で細かく見れますsun.gc.metaspacesun.gc.compressedclassspace
$ /usr/local/jdk1.8.0_05/bin/jcmd 7831 PerfCounter.print :sun.gc.compressedclassspace.capacity=524288sun.gc.compressedclassspace.maxCapacity=1073741824sun.gc.compressedclassspace.minCapacity=0sun.gc.compressedclassspace.used=264208 :sun.gc.metaspace.capacity=4980736sun.gc.metaspace.maxCapacity=1082130432sun.gc.metaspace.minCapacity=0sun.gc.metaspace.used=2434808 :
jconsole
各メモリプールから確認できます
VisualVM
Metaspace全体の確認ができます
JMX
各メモリプールがあります
GC ログ Permだったところが Metaspace に変わって
います$ /usr/local/jdk1.8.0_05/bin/java -XX:+PrintGCDetails SystemGC :[Full GC (System.gc()) [PSYoungGen: 368K->0K(1024K)] [ParOldGen: 8K->251K(59904K)] 376K->251K(60928K), [Metaspace: 2377K->2377K(1056768K)], 0.0047055 secs][Times: user=0.01 sys=0.00, real=0.00 secs] : ※見やすく改行しています
Native Memory Tracking (NMT)
細かい情報を簡単に知りたいときに最適 -XX:NativeMemoryTracking=[summary|detail] 取り方は 2 種類
-XX:+UnlockDiagnosticVMOptions と-XX:+PrintNMTStatistics で java終了時に取得
jcmd <PID> VM.native_memory で外側から取得 Metaspace は” Class” の部分です
jcmd VM.native_memory
$ /usr/local/jdk1.8.0_05/bin/jcmd 8858 VM.native_memory8858:
Native Memory Tracking:
Total: reserved=2330153KB, committed=135365KB :- Class (reserved=1062006KB, committed=10102KB) (classes #374) (malloc=5238KB, #153) (mmap: reserved=1056768KB, committed=4864KB) :
インスタンスクラス数
HeapStats
1.1系を使ってください 閾値監視( SNMP Trap送信)もできます
ココ
注意 Metaspace の値は CompressedClassSpace
の値も含む、全体的な値です。CompressedClassSpace Metaspace⊆
NonClassType な Metaspace を見たい場合は、Metaspace から CompressedClassSpace分を引いてください。
Metaspace関連オプション
Metaspace 関連オプション( -XX )オプション 意味 デフォルトMinMetaspaceExpansion Metaspace の最小拡張単
位256KB
MinMetaspaceFreeRatio GC 後の最小フリー量のパーセンテージ
40
MaxMetaspaceFreeRatio GC 後の最大フリー量のパーセンテージ
70
MaxMetaspaceExpansion Metaspace の最大拡張単位
4MB
Metaspace 関連オプション( -XX )オプション 意味 デフォルトUseLargePagesInMetaspace Metaspace にラージペー
ジを使うか?( UseLargePages 必須)
false
TraceMetadataHumongousAllocation 大きなオブジェクトをMetaspace にアロケートするのをトレースする
false
InitialBootClassLoaderMetaspaceSize ブートクラスローダ用Metaspace の初期値
LP64 : 4MB
それ以外:2200KB
MetaspaceSize Metaspace をリサイズさせるための閾値
環境依存
MaxMetaspaceSize 最大 Metaspace サイズ unsigned long最大値
CompressedClassSpaceSize CompressedClassSpaceのサイズ
1GB
-XX:MetaspaceSize ではMetaspace の初期サイズを
指定できません!
hotspot-gc-dev に質問してみたhttp://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2014-April/009853.html
JIRA にも登録しました: JDK-8039867: Incorrect description: -XX:MetaspaceSize https://bugs.openjdk.java.net/browse/JDK-8039867
チューニング(?)
オプションをつける前に… Metaspace は連続空間ではない 1 クラスローダ 1 メタスペース
ブートストラップ以外のクラスローダの初期サイズは変更できない○ InitialBootClassLoaderMetaspaceSize (デフォルト
4MB ) CompressedClassSpace はクラス情報「だけ」
シンボルやメソッドプロファイル情報などは別領域 長時間動作させていれば、 Metaspace 使用量はあ
る程度安定してくる(はず)動的ロードをガンガン行う場合はワカラナイ…
一番注意すべきオプションCompressedClassSpaceSize
JDK7u55 : VmSize: 2153520 kB
JDK8u5 : VmSize: 3390320 kB
約 1GB の差!
オプションを何もつけずに起動したときの仮想メモリ使用量( VSZ )
なぜ 1GB も差が? AMD64 な JDK7 では MaxPermSize の初期値
が 64MB ( jdk7u-dev の HotSpot の場合)Perm目的で 64MB しかリザーブしない
○ MaxPermSize はプラットフォームや HotSpot VM の種類によりデフォルト値が異なります
CompressedClassSpace のデフォルトは 1GB1GB リザーブしてしまう!!
デフォルトのまま使うときはオーバーコミットに注意しましょう!
MaxMetaspaceSize
青天井!OS に殺されるかも
…
古の情報http://hg.openjdk.java.net/hsx/hotspot-rt/hotspot/diff/740e263c80c6/src/share/vm/runtime/arguments.cpp#l1.83
昔は 1 クラス情報= 1KB とみなしていました CompressedClassSpace をデフォルトで 100MB
に昔は ClassMetaspaceSize というオプションでした100,000 クラスはロードできるよ!という話
1 クラスあたりの占有サイズ フィールド数、メソッド数によって異なります 一概に「だいたい xx バイト」と表現できません
実測一番!
と、いうわけで…
-XX:MaxMetaspaceSize= そこそこ-XX:CompressedClassSpaceSize=100m
※責任はもちません
-XX:MaxMetaspaceSize= そこそこ
①-Xmx32g 以下の場合
②-Xmx32g超えの場合
③もう何も気にしたくない場合
-XX:-UseCompressedClassPointers
?