GC の基本的な機能は、プログラムで作ったオブジェクトの、 その時点での使用、未使用を決定し、すでに未使用になった領域を回収します。
使用、未使用の決定は、heap にあるオブジェクトで参照がないものにマークをつけ、 実行中の全スレッドを一旦停止して (stop-the-world)、 使用中のオブジェクトを別の heap エリアに移動し、 それらを参照するアドレスをすべて付け替えます。 この方法は、始めから変わっていません。 この処理を効率化するために、どのように改良したかについての説明します。
GC スレッドが動くときは、その他の全スレッドは動けません(stop-the-world)。 それは、GC がオブジェクトを移動させるためで、 その時同時にそれらを参照、更新できないからです。 統計によると、生成されたオブジェクトの 90% 以上がすぐに使われなくなります。 従ってオブジェクトは世代管理 (generation) するのが有効で、 Generational GC と呼ばれています。
JVM heap layout
eden space | from space | to space | young generation |
(2MB) | (64KMB) | (64KMB) | |
tenured space | old generation | ||
(5MB min,44MB max) | |
||
permanent space | permanent generation | ||
(4MB) | |
(*: 各サイズはデフォルト値)
ヒープには young generation、old generation と permanent generation があります。 young generation には eden-space と 2 つの survivor space (s0:from-space,s1:to-space) があります。 オブジェクトはまず eden-space または from-space に確保されます。 通常 to-space は空にしておきます。 この young generation 用の領域は、全体の heap サイズからすると小さい部分です。 ここがいっぱいになると、minor GC が起こります。 従って minor GC は頻繁に行われます。まず eden と from-space のほとんどのオブジェクトはすでに未使用となっていますので、 それらは回収されますが、 まだ使用中のオブジェクトは to-space に移動されます。 しかし to-space がいっぱいになる、または大きなサイズのオブジェクトは old generation にも移動されます。 その後 to と from の名前を交換します。 minor GC が完了すると eden-space は空となり、 そこは新たに作られるオブジェクトのための領域となります。
young generation に長くいるオブジェクトは old generation に移されます。 他方 old generation に残るオブジェクトは、しだいに詰まってきます。 young generation の GC は minor GC、old generation の GC は major GC と呼ばれます。 old generation がいっぱいになると、major GC がおこります。 これは、頻度はすくないですが、時間がかります。
あと1つ permanent generation があり、 これは JVM のクラスやメソッドオブジェクトが使う特別なデータ領域で、 ずっと存在しつづけ、GC の対象にはなりません。
未使用領域は free list に登録されます。 この場合 old generation が断片化 (fragmentation) する問題があります。 そのために compact します。
いくつかの例外があります。 young generation で領域が確保できない場合や、大きなオブジェクトは、 直接 old generation に確保されます。
CMS は GC をマークフェーズとスイープフェーズに分け、 これらを通常のアプリケーションスレッドと同時に行います。
ただし、マークをするときにマーク漏れを防ぐため、 すべてのスレッドをストップさせてリマーク処理を行う必要があります。 しかし、アプリケーションスレッドが止まるのは、 このリマーク処理とスイープが終了した後の初期化の間だけなので、 停止時間は短くなり、高速化しています。
Connection conn = new Connection(); : conn = null;
Public void paint(Graphics g) { g.fillRect(0,0,getSize().width, getSize().height); } ==> Public void paint(Graphics g) { Dimension size = getSize(); g.fillRect(0,0,size.width, size.height); }
static final String s1 = "Hello"; static final String s2 = "Hello"; public static void main(String[] argc) { String s3 = new String("World"); String s4 = new String("World"); boolean b1 = (s1 == s2); // True boolean b2 = (s3 == s4); // False boolean b3 = s3.equals(s4); // True System.out.println("b1 = "+b1+" b2 = "+b2+" b3 = "+b3); }
String s="",s_i; for (i=0; i<MAX_SIZE; i++) { ii = new Integer(i); s_i = ii.toString(); s += s_i; } ==> StringBuffer sb = new StringBuffer(); for (i=0; i<MAX_SIZE; i++) { ii = new Integer(i); sb = sb.append(ii.toString()); }
> java -server JVM for server > java -client JVM for client
参考資料
http://developer.java.sun.com/developer/technicalArticles/Programming/GCPortal http://java.sun.com/products/hotspot/