カウンター

2014年1月3日金曜日

JavaVMのメモリ管理(Permanent領域編)

今回はPermanent領域に焦点を当てていきます。
※あまり経験が無い分野なので、間違っている箇所があるかもしれません。。。その際はご指摘ください。

JavaVMではクラスローダを利用してクラスファイルをPermanent領域に読み込みます。クラスファイルとは、Javaコンパイル時に生成される、中間ファイル(.classファイル)のことです。クラスローダには下記4種類あります。

  • ブートストラップクラスローダ
    • Javaの中核となるライブラリをロードする。ネイティブコード。(ネイティブコードとは、OS上で直接実行可能な形式、つまり機械語になっているものです)


  • 拡張クラスローダ
    • 拡張ディレクトリ(/lib/ext や java.ext.dirsプロパティで指定された他のディレクトリ)にあるコードをロードする。


  • システムクラスローダ
    • CLASSPATH変数に記述されたクラスをロードする。通常、ユーザが記述したコードはこちらのクラスローダによってロードされる。


  • ユーザ定義クラスローダ
    • ユーザが定義したClassLoaderクラスによってロードする。


無論Permanent領域にもGCがあり、当該領域がいっぱいになると発生し、Old領域と同じようにPermanennt領域のGC中はJava全体が止まってしまいます。ちなみに、私が経験したケースだと、Old領域のFullGCは0.2秒程度(コンカレントGC)だったのに対し、Permanent領域のFullGCには7秒もかかっていました。。。

上記のGC時間を見てもわかる通り、Permanent領域については極力防がないといけないものです。前回も記載した通り、定期再起動とかメモリ増設なんですが、今回はアプリケーションの観点で考察したいと思います。

最初に記載したとおり、Permanent領域ってクラスロードに利用されます。基本的には最初(起動時)にクラスをロードしたら一定のはず、、、なんですが、GCが発生するようなPermanent領域が次第に増えていくケースというのは、下記のような場合があると考えられます。

  1. 新規利用されるクラス
  2. ユーザ定義クラスローダ
  3. リフレクション


「1.新規利用されるクラス」

Javaの基本原則として、必要になって初めてクラスがロードされます。長い間稼働するプログラムで、呼び出されることのなかったクラスが呼び出されるとそこで初めてクラスがロードされますので、理論的には時間とともに微増していくと考えられます。

「2.ユーザ定義クラスローダ」

"ClassLoaderクラス"を継承ことによって、ユーザ定義のクラスローダを作成することができます。(ClassLoaderクラスは抽象クラスであるため、直接生成できない)こちらでクラスロードを実行すると、当然Permanent領域を利用することになり、当該領域が増えます。

「3.リフレクション」

リフレクションとは、プログラムの実行過程で自身の構造を読み取ったり書き換えたりする技術のことで、javaではjava6以降で導入されています。リフレクションの機能を利用することで、動的なクラス生成が可能です。例えば、生成したクラスorメソッドを、プログラムに渡す引数によって変えたり等の柔軟な処理が書けたりします。

ただ、一定回数以上の呼び出しがあると、当該クラスをバイトコード化して、Permanent領域に格納するためPermnent領域が増加していく、という現象があるようで、こちらもPermanent領域増加の原因となってしまうようです。

// リフレクション
Class sampleins = Class.forName("Hoge");
Method samplemethod = sampleins.getMethod("helloworld");
samplemethod.invoke(sampleins.newInstance());

Permanent領域のFullGC原因は一概には言えないですが、チューニングに際しては下記オプションが使えそうです。
  • -XX:+ TraceClassloading
    • クラスのロード時に情報を出力
  • -XX:+ TraceClassUnloading
    • クラスのアンロード時に情報を出力


0 件のコメント:

コメントを投稿