ホームページ  >  記事  >  Java  >  JVM の各領域の目的の詳細な説明と、潜在的な例外のコード例

JVM の各領域の目的の詳細な説明と、潜在的な例外のコード例

黄舟
黄舟オリジナル
2017-03-20 10:56:191983ブラウズ

プログラム カウンター

は、バイトコード インタプリタが実行する必要があるバイトコード命令を選択するために使用されます。各スレッドは独立したプログラム カウンタを持ち、スレッドは互いに影響しません。スレッドが Java メソッドを実行している場合、このカウンタは、ネイティブ メソッドを実行している場合、実行中の仮想マシンのバイトコード命令のメモリ アドレスを記録します。カウンタは未定義です。この領域は、JVM 仕様で OOM を持たない唯一の領域です

仮想マシン スタック (ローカル変数空間)

コンパイラーに認識されているさまざまな基本データ型 (boolean、byte、char、short、int、float、long) を格納します、double)、オブジェクト アプリケーション (参照)。 64 ビットの double と long は 2 スロットを占有します。メモリ空間は、メソッドを開始するときに、このメソッドが割り当てる必要があるメモリ容量を完全に決定します。 -Xss

Exception:

StackOverflowError スタックの深さが仮想よりも大きい仮想マシン スタックが動的に拡張できる場合 (現在のほとんどの Java 仮想マシンは動的に拡張できますが、Java 仮想マシンの仕様では固定長の仮想マシン スタックも許可されています)、十分なメモリを拡張できない場合

単一のスレッドでは、スタック フレームが大きすぎるか、仮想マシンのスタック容量が小さすぎるかに関係なく、メモリを割り当てることができない場合、仮想マシンは StackOverflowError をスローします

/**
* VM Args:-Xss128k
* 
* stack length:2402 Exception in thread "main" java.lang.StackOverflowError
*/
public class JavaVMStackSOF {

private int stackLength = 1;

public void stackLeak() {
    stackLength++;
    stackLeak();
}

public static void main(String[] args) throws Throwable {
    JavaVMStackSOF oom = new JavaVMStackSOF();
    try {
        oom.stackLeak();
    } catch (Throwable e) {
        System.out.println("stack length:" + oom.stackLength);
        throw e;
    }
}
}

テストが 1 つのスレッドに限定されていない場合スレッド、連続経由 スレッドの作成方法により、メモリ オーバーフロー例外が発生する可能性があります。ただし、この方法で生成されるメモリ オーバーフロー例外は、占有領域が十分に大きいかどうかとは関係がありません。正確に言えば、この場合、各スレッドのスタックに割り当てられるメモリが大きいほど、メモリ オーバーフロー例外が生成されやすくなります。 。

オペレーティング システムによって各プロセスに割り当てられるメモリは制限されているため、たとえば、32 ビット ウィンドウは 2GB に制限されます。このテストは、多数のスレッドを作成することによって行われます。各スレッドがスタック メモリを占有し、大量のメモリを割り当てるため、システムに十分なメモリが不足します。自動的に展開することはできません

/**
 * VM Args:-Xss2M (这时候不妨设大些)
 *
 * java.lang.OutOfMemoryError:unable to create new native thread
 */
public class JavaVMStackOOM {

       private void dontStop() {
              while (true) {
              }
       }

       public void stackLeakByThread() {
              while (true) {
                     Thread thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                   dontStop();
                            }
                     });
                     thread.start();
              }
       }

       public static void main(String[] args) throws Throwable {
              JavaVMStackOOM oom = new JavaVMStackOOM();
              oom.stackLeakByThread();
       }
}

ネイティブ メソッド スタック

は、一方が Java メソッドを実行する仮想マシンであり、もう一方がネイティブ メソッドを実行している点を除いて、仮想マシン スタックと似ています

例外:

StackOverflowError スタックの深さ仮想マシンで許容される深さよりも大きいです

OOM

Javaヒープ

メモリリサイクルの観点から、基本的に世代別収集アルゴリズムが使用されるため、新しい世代と新しい世代に分割されます。古い世代。細かく分けると、Eden 空間、From Survivor 空間、To Survivor 空間などに分けることができます。 -Xmx -Xms はヒープ領域のサイズを制御します

例外: ヒープを拡張できない場合は

1.OOM

/**
 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * 
 * java.lang.OutOfMemoryError: Java heap space
 */
public class HeapOOM {

    static class OOMObject {
    }

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<OOMObject>();

        while (true) {
            list.add(new OOMObject());
        }
    }
}

メソッド領域

スレッド間で共有されます。仮想マシンに読み込まれたクラス情報、定数、静的変数、インスタントエディタでコンパイルしたコードなどのデータを保存します。これは、HotSpot 仮想マシンにおける不滅世代と言えます。

1.6 以前では、ランタイム定数はメソッド領域の一部です (String.intern() は定数プールに動的に追加されます) -XX:MaxPermSize によってサイズが制御されます。 JDK1.7 以降のバージョンでは、これは Java ヒープ内で開かれたメモリの一部です

例外状況:

OOM

/**
* 需要在JDK1.6上才能复现,JDK1.7及之后版本的JVM已经将运行时常量池从方法区中移了出来,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。
* 在JDK1.7上运行的效果则会一直执行,直到堆内存使用完毕
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M 
*
* java.lang.OutOfMemoryError:PermGen space
*/
public class RuntimeConstantPoolOOM {

public static void main(String[] args) {
    // 使用List保持着常量池引用,避免Full GC回收常量池行为
    List<String> list = new ArrayList<String>();
    // 10MB的PermSize在integer范围内足够产生OOM了
    int i = 0;
    while (true) {
        list.add(String.valueOf(i++).intern());
    }
}
}
/**
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
* java.lang.OutOfMemoryError:PermGen space
* 一直创建动态类
*/
public class JavaMethodAreaOOM {

public static void main(String[] args) {
    while (true) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OOMObject.class);
        enhancer.setUseCache(false);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                return proxy.invokeSuper(obj, args);
            }
        });
        enhancer.create();
    }
}

static class OOMObject {

}
}

ダイレクト メモリ (仮想マシンの実行中にデータ領域の一部ではありません)

NIO は次のことができますネイティブ関数を使用する ライブラリは外部メモリを直接割り当て、このメモリへの参照として Java ペアに格納されている DirectByteBuffer オブジェクトを通じて動作します。マシンの物理メモリによって制限され、-XX:MaxDirectMemorySize で指定できます。指定しない場合、デフォルトは Java ヒープの最大値 (-Xmx) と同じになります。

例外:

1.OOM

/**
 * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
 * 
 * java.lang.OutOfMemoryError
 */
public class DirectMemoryOOM {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(_1MB);
        }
    }
}

以上がJVM の各領域の目的の詳細な説明と、潜在的な例外のコード例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。