Java仮想マシンの研究ノート1の詳細な理解(Javaメモリ領域とメモリオーバーフローの例外)

1.Java仮想マシンの実行時のデータ領域


すべてのスレッドで共有されるデータ領域:ヒープ、メソッド領域
スレッド分離データ領域:VMスタック、ネイティブメソッドスタック、プログラムカウンターレジスタ
1.1概念モデルでは、バイトコードインタープリターは、現在のスレッドのプログラムカウンターの値を変更することによって機能します。実行する次のバイトコード命令を選択します
1.2仮想マシンスタックは、仮想マシンにjavaメソッドの実行を提供します。各メソッドが実行されると、ローカル変数テーブル、操作スタック、動的リンク、およびメソッド出口を格納するフレームスタックが作成されます。 。およびその他の情報。
各メソッドが呼び出されてから実行が完了するまでのプロセスは、仮想マシンスタックでのプッシュからポップまでのフレームスタックのプロセスに対応します。
1.3ローカルメソッドスタックは、仮想マシンのローカルメソッドサービスを実行します
。1.4仮想マシンの起動時にヒープが作成されます。このメモリ領域の唯一の目的は、オブジェクトインスタンスを格納することです。ヒープは、新世代と旧世代に分割できます。生成
。1.5仮想マシンを格納するためのメソッド領域クラス情報、定数、静的変数、マシンによってロードされたジャストインタイムコンパイラによってコンパイルされたコードなどのデータ。

2.オブジェクトアクセス

2つの主流のオブジェクトアクセス方法があります:ハンドルと直接ポインタの使用

3.実際の戦闘機は異常です

3.1javaヒープオーバーフロー

/**
 * @Title: HeapOOM
 * @Description: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/home/
 * @date 2020/4/15 17:57
 */
public class HeapOOM {
    static class OOMObject{}

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

演算結果:

3.2仮想マシンスタックとネイティブメソッドスタックのオーバーフロー

/**
 * @Title: JavaVMStackSOF
 * @Description: -verbose:gc -Xms20M -Xss128K -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/home/
 * @date 2020/4/16 9:08
 */
public class JavaVMStackSOF {
    private int stackLength = 1;

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

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

操作の結果はStackOverflowErrorのみです

3.3スレッドを作成すると、メモリオーバーフローが発生します

/**
 * @Title: javaVMStackOOM
 * @Description: -verbose:gc -Xms20M -Xss2M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/home/
 * @date 2020/4/16 9:33
 */
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) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

実行後にクラッシュするのは簡単で、実行しても結果はありません。

3.4ランタイム定数プールオーバーフロー

ドキュメント2.4.3に従ってメソッド領域のオーバーフローコードをテストしたところ、長い時間が経過し、メモリオーバーフローが発生していなかったことがわかりました。ドキュメントを読んだ後、jdk1.7の後に永続コードが削除されたことがわかりました。

//-XX:PermSize = 10M -XX:MaxPermSize = 10M 
public class RuntimeConstantPoolOOM { 
    public static void main(String [] args){ 
        List <String> list = new ArrayList <>(); 
        int i = 0; 
        while(true){ 
            list.add(String.valueOf(i ++)。intern()); 
        } 
    } 
}

ここまでは、ネイティブメモリに格納されているジャストインタイムのコンパイル済みコードに加えて、クラス情報、定数、静的変数、定数プールなどの他のデータが永続世代に格納されていますが、このように、つまり、メモリリークが発生しやすくなります。

したがって、jdk8では、永続的な世代が完全に削除され、metaSpaceに置き換えられます。メソッド領域の概念はメタスペースに保持され、定数プールはヒープにあります。

メソッド領域JVM仕様で定義されている概念にすぎません。クラス情報、定数プール、静的変数、JITコンパイル済みコードなどのデータを格納するために使用されます。メソッド領域を配置すると、さまざまな実装をさまざまな場所に配置できます。恒久的な世代があるユニークなコンセプトホットスポット、他のJVMで使用できないメソッドエリアの実装である仮想マシン、。

jdk1.7より前:メソッド領域は永続世代(PermGen)にあります。永続世代とヒープは互いに分離されています。永続世代のサイズは、JVMの起動時に固定値に設定できます。不変です
。jdk.7:永続世代に格納されているデータの一部は、Javaヒープまたはネイティブメモリに転送されたばかりです。ただし、永続的な生成はJDK 1.7にまだ存在し、完全に削除されていません。たとえば、シンボル参照(シンボル)はネイティブメモリに転送され、インターンされた文字列はJavaヒープに転送され、クラス静的変数はJavaヒープに転送されます。 ;
jdk1.8:メソッド領域の概念は引き続き保持されますが、実装は異なります。永続的な生成をキャンセルすると、メソッドはメタスペースに格納されます。メタスペースはまだヒープに接続されていませんが、論理的にヒープ内にあると見なすことができるヒープと物理メモリを共有しています。

1)永続世代(PermGen)を削除し、
メタスペースに置き換えました。2 )永続世代のクラスメタデータをネイティブメモリ(仮想マシンではなくローカルメモリ
)に転送しました。3)インターン文字列と永続世代クラス静的変数Javaヒープに転送されます
。4)永続生成パラメーター(PermSize MaxPermSize)->メタスペースパラメーター(MetaspaceSize MaxMetaspaceSize)。

3.5メソッド領域のオーバーフロー

public class JavaMethodAreaOOM { 
    static class OOMObject {} 

    public static void main(String [] args){ 
        while(true){ 
            Enhancer enHancer = new Enhancer(); 
            enHancer.setSuperclass(OOMObject.class); 
            enHancer.setUseCache(false); 
            enHancer.setCallback(新しいMethodInterceptorの(){ 
                @Override
                パブリックオブジェクトインターセプト(オブジェクトo、メソッドのメソッド、オブジェクト[]引数、MethodProxy methodProxy)のThrowableを{スロー
                    methodProxy.invoke(O、引数)を返します; 
                } 
            })。
        } 
    } 
}

jdk1.7仮想マシンパラメータ:-XX:PermSize = 10M -XX:MaxPermSize = 10M 

演算結果:

jdk1.8仮想マシンパラメータ:-XX:MetaspaceSize = 10M -XX:MaxMetaspaceSize = 10M

演算結果:

jdk1.7演算結果のヒープオーバーフローは、クラス情報が実際にヒープに格納されていることを示しています。

jdk1.8の実行結果のメタスペースオーバーフローは、クラス情報が実際にメタスペースに配置されていることを示しています。

3.6ネイティブダイレクトメモリオーバーフロー

//-Xmx20M -XX:MaxDirectMemorySize = 10M 
public class DirectMemoryOOM { 
    private static final int _1M = 1024 * 1024; 

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

演算結果:

おすすめ

転載: blog.csdn.net/noob9527/article/details/105559659