ガーベジ・コレクション:GC ( Garbage Collection ) についての簡単な説明と調査方法
技術情報 TOP へ

Java の GC について簡単に説明いたします。


GC はヒープやヒープサイズと密接な関連があります。以下のページも合わせて参照ください。

「Java のヒープサイズ」についての簡単な説明


Java プログラムが動作するとオブジェクトはメモリ上にロードされます。

大きなオブジェクトを使用したり、また、使用するオブジェクトの数が多ければ、その分メモリの使用領域は増加します。
そのまま、新しいオブジェクトをロードし続けると、Java が使用できるメモリ領域がメモリが一杯になります。

* 「 Java が使用できるメモリ領域 」、これをヒープ領域と言います。( ヒープ領域以外にも Permanent 領域が存在します。)

メモリが一杯になると新しいオブジェクトをロードできず、プログラムを実行することができなくなります。

このような状態を回避するための仕組みが ガーベジ・コレクション:GC ( Garbage Collection ) です。

GC は、ヒープ領域に空きが少なくなると使用されるオブジェクトと使用されないオブジェクトを判別して、使用されないオブジェクトをゴミとして掃除 ( メモリ上から消去 ) して、空き領域を増加させる処理を行います。

Sun や HP のJVM では 世代別 GC と呼ばれるアルゴリズムで管理されており、ヒープ領域を若い世代 ( Young Generation :New 領域 ) と古い世代 ( Tenured Generation もしくは Old Generation とも呼ばれます : Old 領域 ) に分けられます。
他に永続的世代 ( Permanent Generation : Permanent 領域 ) が存在します。

Java の GC は上記の世代別ヒープ領域のどこを対象にするかで、大きく分けて以下の 2 種類が存在します。

「Scavenge GC」
New 領域が不足した場合に実行され、主に New 領域が対象です。比較的、頻繁に行われ、短い時間で処理が終わります。

「Full GC」
New 領域、Old 領域、Permanent 領域が不足した場合に実行されます。

大きな重い処理であるため処理時間もかかります。また、Full GC が行われている間は、他の処理ができないためシステムは停止状態となります。

上述の内容からもわかりますが、システムパフォーマンスに大きな影響を与える Full GC の発生は可能な限り避ける必要があります。

Full GC が頻発するシステムはシステム仕様にもよりますが、一般的には問題視する必要もあります。

Full GC が頻発し続けると OutOfMemoryError が発生 ( JVM が使用できるメモリが不足して処理が続行できない状態 ) する可能性もあります。




GC の状態を確認する方法としては、Java の起動オプションを使用する方法とツールを利用する方法があります。 ここでは Java の起動オプションを使用して、GC の状態を確認してみます。

GC に関連する主な Java の起動オプションを以下に挙げました。

-verbose:gc一般的な GC 情報を出力
-Xloggc:filenameGC の出力情報をファイル filename に保存
-XX:+PrintGCDetailsNew 領域、Old 領域の詳細情報を出力
-XX:+PrintTenuringDistributionオブジェクトの寿命の情報
-XX:+PrintHeapAtGCGC 前後のヒープ領域の情報
-XX:+TraceGen0TimeNew 領域の GC の回数、総時間、平均時間
-XX:+TraceGen1TimeOld 領域の GC の回数、総時間、平均時間

* Java 1.3.1 系では上記の内、「-verbose:gc」しか使用できません。

一般的によく使用されるのは、「-verbose:gc」もしくは「-Xloggc:filename」と「-XX:+PrintGCDetails」等の組み合わせが多いようです。



以下、このページでは、簡単なプログラムを使用して GC の状態を確認していきます。

まず、以下の簡単な検証用プログラムを用意します。ここでは簡単な Swing を使用します。

ファイル名:SampleTable.java
import java.awt.*;
import javax.swing.*;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.JTableHeader;

public class SampleTable extends JFrame {
	
  private JPanel contentPane = null;  
  private JScrollPane jScrollPane1 = null;  
  private JTable jTable = null;
   
  public SampleTable() {   
       try {
       	init();
       }
       catch(Exception e) {
         e.printStackTrace();
       }
  }
       
  public static void main(String args[]){
	  new SampleTable();
  }       
  
  private void init() throws Exception  {	  
	  
       contentPane = (JPanel) this.getContentPane();
       contentPane.setLayout(new BorderLayout());       
       jScrollPane1 = new JScrollPane(getJTable());       
       contentPane.add("Center",jScrollPane1);        
       this.setSize(new Dimension(500, 300));
       this.setTitle("テスト");         
       this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       this.setVisible(true);     
  }  

  private void initTable() {		
       String[] colnames = {"col1","col2","col3","col4","col5",
    		"col6","col7","col8","col9","col10",};
       Object[][] data = new Object[50][50];

       for(int n= 0; n < 10;n++){
       	  for(int m= 0; m < 50;m++){
	      data[m][n] = "○";
       	  }
       }
       
       jTable = new JTable(data,colnames);	         
    
       DefaultTableColumnModel cmodel =
          (DefaultTableColumnModel)jTable.getColumnModel();
              
       for(int i= 0; i < colnames.length;i++){
          cmodel.getColumn(i).setPreferredWidth(100);
       }       
    }

  public JTable getJTable() {
    if(jTable == null){
		initTable();
    }
   return jTable;
  }  
}




まずは、-verbose:gc を使用してみます。

以下のバッチファイルを作成します。
javac SampleTable.java
java -verbose:gc SampleTable
pause
上記のバッチファイルを実行すると以下のような出力が得られました。
[GC 512K->276K(1984K), 0.0048112 secs]
[GC 780K->517K(1984K), 0.0067000 secs]
[GC 625K->566K(1984K), 0.0033630 secs]
[GC 1078K->1077K(1984K), 0.0016818 secs]
上記の例では 4 回の GC が発生しています。

上記の例より以下のデータ ( 1 行目のデータ ) を基に情報の説明を記述します。

[GC 512K->276K(1984K), 0.0048112 secs]

■ GC ・・・GC の種類を表示します。
「GC」 と表示されれば主に New 領域が対象の Scavenge GC です。
「FULL GC」と表示されれば New 、Old 領域が対象の FULL GC です。
■ 512K ・・・GC 前のオブジェクトのサイズ
■ 276K ・・・GC 後のオブジェクトのサイズ
■ (1984K)・・全体のヒープ領域 ( New 領域 )
■ 0.0048112 secs・・・この GC の処理時間





次に「-Xloggc:filename」と「-XX:+PrintGCDetails」の組み合わせを任意のヒープサイズを指定して使用してみます。

ここで使用するヒープ領域設定は以下のとおりです。 ( 簡単な例なので少しヒープ領域設定は小さめにしています。 )

-Xms ヒープ全体の初期値 16MB
-Xms ヒープ全体の最大値 16MB
-XX:NewSize New 領域の初期値 4MB
-XX:MaxNewSize New 領域の最大値 4MB

Java のヒープについては、以下のページを参照ください。

「Java のヒープサイズ」についての簡単な説明


以下のバッチファイルを作成します。
javac SampleTable.java
java -Xmx16m -Xms16m -XX:NewSize=4m -XX:MaxNewSize=4m -Xloggc:./gc.txt -XX:+PrintGCDetails SampleTable
pause
上記のバッチファイルを実行すると以下のような出力が得られました。
3.125: [GC 3.125: [DefNew: 3328K->384K(3712K), 0.0112500 secs] 8168K->5998K(16000K), 0.0113291 secs]
4.539: [GC 4.539: [DefNew: 3712K->4K(3712K), 0.0032661 secs] 9326K->5999K(16000K), 0.0033515 secs]
10.668: [GC 10.669: [DefNew: 3332K->3K(3712K), 0.0015267 secs] 9327K->5999K(16000K), 0.0015929 secs]
12.103: [GC 12.103: [DefNew: 3331K->3K(3712K), 0.0003727 secs] 9327K->5999K(16000K), 0.0004336 secs]
13.539: [GC 13.539: [DefNew: 3331K->4K(3712K), 0.0003766 secs] 9327K->5999K(16000K), 0.0004347 secs]
18.199: [GC 18.199: [DefNew: 3332K->10K(3712K), 0.0012516 secs] 9327K->6005K(16000K), 0.0013153 secs]

上記の例では 6 回の GC が発生しています。

上記の例より以下のデータ ( 1 行目のデータ ) を基に情報の説明を記述します。

3.125: [GC 3.125: [DefNew: 3328K->384K(3712K), 0.0112500 secs] 8168K->5998K(16000K), 0.0113291 secs]

上記のデータの説明

■ 3.125・・・JVM が起動してから GC が発生した時間
■ GC   ・・・GC の種類を表示します。
「GC」 と表示されれば主に New 領域が対象の Scavenge GC です。
「FULL GC」と表示されれば New 、Old 、Permanent 領域が対象の FULL GC です。

New 領域の情報
■ DefNew・・・New 領域に対するGCを表す。以下の値はNew 領域が対象。
■ 3328K ・・・GC 前のオブジェクトのサイズ ( New 領域 )
■ 384K  ・・・GC 後のオブジェクトのサイズ ( New 領域 )
■ (3712K) ・・New 領域のヒープサイズ 
■ 0.0112500 secs・・・この New 領域の GCにかかった時間

ヒープ全体の情報
■ 8168K ・・・GC 前のオブジェクトのサイズ
■ 5998K ・・・GC 後のオブジェクトのサイズ
■ (16000K)・・全体のヒープサイズ
■ 0.0113291 secs・・・この GC にかかった時間


GC のログ出力の見方は上記で説明したとおりですが、上記の例だけでは、状態の良、悪の判断がつかないかもしれません。 上記の例は、サンプルが単純なので当然ですが、どちらかというと良好な GC の状態です。
そこで、以下に悪い例を挙げますので、GC のログの分析をされる際の参考になればと思います。



以下で紹介する例は、上述のサンプルプログラムとはシステムの規模、ヒープ設定などは全く違います。

[ 悪い例 ]

以下は大きな規模のシステムで Full GC が発生している例です。上記の例とシステムやヒープ設定が違うので単純に比較はできませんが、 Full GC が発生している悪い例として参考になると思います。

以下の出力例を見てみましょう。

210.274: [GC 210.274: [DefNew: 335145K->3122K(339264K), 0.1164251 secs] 
	389309K->62953K(1038336K), 0.1165947 secs]
214.007: [Full GC 214.007: [Tenured: 68230K->67535K(699072K), 1.2545997 secs] 
	167459K->64535K(1038336K), [Perm : 53247K->53247K(53248K)], 1.2547773 secs]

上記のデータの説明

■ 210.274 及び 214.007・・・JVM が起動してから GC が発生した時間
■ Full GC ・・・GC の種類を表示します。
「GC」 と表示されれば主に New 領域が対象の Scavenge GC です。
「FULL GC」と表示されれば New 、Old 、Permanent 領域が対象の FULL GC です。

この例では、210.274 のデータは GC 、214.007 のデータで Full GC が発生しています。

210.274 のデータ ( 1 行目のデータ ) は上述の説明を参考にしていただき、
ここでは、214.007 のデータ ( 2 行目のデータ ) について確認していきます。

Old 領域の情報
■ Tenured・・・Old 領域に対するGCを表す。以下の値は Old 領域が対象。
■ 68230K ・・・GC 前のオブジェクトのサイズ ( Old 領域 )
■ 67535K ・・・GC 後のオブジェクトのサイズ( Old 領域 )
■ (699072K)・・Old 領域のヒープサイズ
■ 1.2545997 secs・・・この Old 領域の GCにかかった時間

ヒープ全体の情報
■ 167459K ・・・GC 前のオブジェクトのサイズ
■ 64535K  ・・・GC 後のオブジェクトのサイズ
■ (1038336K)・・全体のヒープサイズ
■ 1.2547773 secs・・・この GC にかかった時間

Permanent 領域の情報
■ Perm   ・・・Permanent 領域に対するGCを表す。以下の値は Permanent 領域が対象。
■ 53247K ・・・GC 前のオブジェクトのサイズ ( Permanent 領域 )
■ 53247K ・・・GC 後のオブジェクトのサイズ( Permanent 領域 )
■ (53248K) ・・Permanent 領域のサイズ( Permanent 領域 )

[ 上記の例について ]

GC にかかった時間を見ると Full GC はかなりの処理時間がかかることがわかります。( Full GC 中、システムは停止状態になります。 )

Full GC が発生すること自体も問題ですが、上記の例では New 領域、Old 領域、Permanent 領域すべて、 領域一杯まで、サイズが膨らんでいます。また、GC 後もオブジェクトのサイズがあまり減らず、使用可能領域が増えていません。

このまま、このシステムを動作させつづけると Full GC が頻発しつづけ、OutOfMemoryError が発生 ( JVM が使用できるメモリが不足して処理が続行できない状態 ) するかもしれません。

このような場合は、絶対的なヒープサイズが不足している可能性があります。
しかし、ヒープサイズを増加しても、また同じ状況になる場合、メモリリークの疑いもあります。

いずれにしても、上記のような状態ではシステムはまともに動作しません。

正確な原因は調査を行い、システムの状態を分析する必要があります。

また、GC のデータは一部だけを見るのではなく、起動時からどのような挙動がつづくのかを確認する必要があります。



極力 Full GC を抑えるには以下の点に注意することが一般的です。

・オブジェクトをできるだけ使い回さないこと。
( オブジェクトの寿命が長くなるため、いずれ Old 領域に割り当てられることになり、 それらの数が増えると Full GC が発生しやすくなります。)

・新しいオブジェクトを大量に使用する場合は、New 領域を大きめにとること。
( Old 領域に割り当てられるオブジェクトを少なくする。 )

Java の GC については以下のページに詳細なドキュメントがあります。

・Java HotSpot Garbage Collection
http://java.sun.com/javase/technologies/hotspot/gc/index.jsp


・Tuning Garbage Collection with the 5.0 Java Virtual Machine
http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html


・Frequently Asked Questions
http://java.sun.com/docs/hotspot/gc1.4.2/faq.html

技術情報 TOP へ


Google
WWW を検索 whitemark.co.jp を検索
[ 株式会社ホワイトマーク TOP ]   [ 免責事項 ]

Copyright © 2006 by WhiteMark, All rights Reserved. Last Modified: 2006/05