Java仮想マシン[JVM]

1メモリ領域に(ランタイムデータ領域)

  • プライベートスレッド:
    プログラムカウンタ、Java仮想マシン・スタック、ネイティブメソッドスタックを
  • スレッド共有:
    ヒープを、メソッド領域(実行時定数プール)、ダイレクトメモリ

    1.1プログラムカウンタ

    実行中のプログラムを記録するために使用されるバイトコード命令のアドレス

    1.2仮想マシンのスタック

  • 組成:
    Javaメソッドの各実装は、スタックフレームを作成し、各スタックフレームは、ローカル変数テーブル、オペランドスタック、定数プール参照を。
    オペランドスタック:計算の前と後に、データを格納するための、ポップスタック
  • 例外:
    ときに要求されたスレッドのスタックの深さが最大値を超え、にStackOverflowError例外がスローされ、
    場合スタックは動的に拡張されるときに十分なメモリを適用することができない、のOutOfMemoryErrorをスローします。

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

    ローカル方法は、一般に、ネイティブメソッドで記述された他の言語(C、C ++、又はアセンブリ言語、等)に使用されるローカルサービス方法スタック。

    1.4ヒープ

    また、GCヒープと呼ばれるガベージコレクションのメインエリア、
  • 作曲:
    新世代(、遺族からは、サバイバーへエデン )、 古い年()
  • 異常:
    OutOfMemoryErrorが発生異常

    1.5メソッド領域(永久世代)

  • 組成:
    店舗クラス情報に用いられる定数は、静的変数は、時間コンパイラはコードやその他のデータ、ならびに実行時定数プールをコンパイルするために、ロードされています。
  • 異常:
    OutOfMemoryErrorが発生異常

JDK 1.8の当初から、恒久的な生成方法およびローカルメモリに配置された空間、それを移動するための元ではなく、仮想マシンのメモリを取り外します。元のデータに代わって、それは永久にヒープとメタスペースに割り当てられています。スタックに等要素の空間型、スタティック変数および定数プールを格納メタ情報。

1.6定数プール

リテラル(リテラル)とシンボリック参照量(シンボリック参照):定数プールは、主に、定数の二つのタイプを格納するために使用されます。

  • リテラル
    ように最終的な定数値として宣言され、そのようなテキスト文字列と同等のJava言語一定レベルの概念、。
  • 符号
    定数以下の3種類を含むコンパイルの原理に属する概念的態様:
    (1)クラスとインタフェースの完全修飾名、及び
    (2)フィールド名と記述子;
    (3)メソッド名と記述。

    1.7ダイレクトメモリ

    JDK 1.4 NIO新しく導入されたクラスでは、それが割り当てられたヒープメモリの外部に直接ネイティブライブラリを使用することができ、その後、JavaヒープDirectByteBufferメモリを参照し、このオブジェクトが動作します。

2.ガベージコレクション

主にヒープ(新生代才)とメソッド領域(永久代理)について

2.1ごみとは何ですか

  • 参照カウント(回復することはないにつながる、お互いを参照してください)
  • 到達可能性分析
    の出発点として、GCのルーツと、検索、REACHは回復することはできません。GC根とおり
    で参照されるオブジェクト(1)VMスタックローカル変数テーブルは、
    (2)ネイティブメソッドスタックJNI参照オブジェクト
    (3)オブジェクトのメソッド領域クラスの静的属性の参考文献
    (4)メソッド領域定数参照されるオブジェクト
  • 参照4種類の:
    (1)強い参照
    (2)ソフト(のみ低メモリ回復)を引用した
    (3)弱参照は、(次のガベージコレクション中に回収される)は、
    (4)参照仮想(ダミーオブジェクトを基準として設定されていますオブジェクトが回収されたときにシステムの唯一の目的は、通知を受信することができます。)

    2.2ガベージコレクションアルゴリズム

  • マーク - クリア
  • マーク - 仕上げ
  • コピー(新世代のヒープ)
  • 世代別コレクション
    新生代:コピーし
    、古い年:タグまたはタグがクリア整理します

    2.3ガベージコレクタ

    (1)Serial 
    串行、新生代、客户端(默认客户端新生代的收集器)、标记整理
    (2)ParNew
    并行、新生代、服务端(Serial的并行版),只能与CMS配合使用、标记整理
    (3)Parallel Scavenge
    并行、新生代、吞吐量优先(降低了并发数,其他收集器是降低用户线程的停顿时间,提升并发数;减少新生代空间——>垃圾回收频繁——>吞吐量下降,缩短吞吐量牺牲了吞吐量,减少了新生代空间)
    (4)Serial Old
    串行、老年代、客户端
    (5)Parallel Old
    并行、老年代、(服务端后台)
    (6)CMS(Concurrent Mark Sweep并发标记清除)
    并行、老年代、服务端
    低停顿
    过程:初始标记(需要停顿)、并发标记、重新标记(解决并发标记期间发生变化的标记,需要停顿)、并发清除(期间发生引用变化会导致浮动垃圾,只能下一次GC再清理)
    缺点:吞吐量低、无法处理浮动垃圾需要临时使用Serial Old处理、标记清除算法会导致不连续空间的浪费
    (7)G1
    服务端
    引入Region
    过程:初识标记、并发标记、最终标记、筛选回收

    !2.4 内存分配回收策略

  • Minor GC:回收新生代,发生一次Minor GC Eden的对象进入Survivor,年龄增加一岁
  • Full GC:回收老年代和新生代
  • 内存分配策略:
    (1)对象优先分配在Eden
    (2)大对象直接进入老年代(-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配)
    (3)长期存活的对象进入老年代(-XX:MaxTenuringThreshold 用来定义年龄的阈值)
    (4)动态年龄判断(如果Survivor相同年龄的对象大于Survivor空间的一半,大于等于该年龄的直接进入老年代)
    (5)空间分配担保
    MinorGC之前,老年代最大可用连续空间大于新生代所有对象大小,则安全,否则不安全,出发空间分配担保的检查。
    如果允许担保失败,则判断老年代的连续空间大于之前晋升到老年代对象的平均大小,如果大于则进行尝试MinorGC;
    否则,担保失败,或者老年代空间较小,都会需要先进行一次FullGC

    2.5 GC触发的条件

    MinorGC:Eden满
    FullGC:
  • System.gc()
  • 老年代空间不足
  • 空间分配担保失败(不允许冒险、或者老年代空间不足担保失败)
  • Concurrent Mode Failure(CMS导致的老年代空间不足)

    3. 类加载机制

    3.1 类加载过程

    (1)加载
    获取二进制字节流——>放入方法区——>生成Class对象放入内存作为方法区的入口
  • 通过类的完全限定名称获取定义该类的二进制字节流。
  • 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。
  • 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。
    (2)验证
    (3)准备
    初始化类变量(static修饰的变量),放在方法区的内存。
public static int value1 = 123;  //初始化为0
public static final int value2 = 123;  //初始化为123

(4)解析
将常量池的符号引用替换为直接引用的过程。
(5)初始化
使用用户设置的值初始化,(上面的代码块中)即将123赋值给value1的过程。
初始化阶段是虚拟机执行类构造器 () 方法的过程。
初始化顺序:

  • 静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。
public class Test {
    static {
        i = 0;                // 给变量赋值可以正常编译通过
        System.out.print(i);  // 这句编译器会提示“非法向前引用”
    }
    static int i = 1;
}
  • 父类中定义的静态语句块的执行要优先于子类。
  • 父接口中的变量,只有在子接口中使用时才会初始化。

    3.2 何时出发类初始化

    类初始化也会执行前面的4个步骤
  • 主动引用:
    (1)new、读取设置静态类变量、调用类的静态方法
    (2)反射调用类
    (3)初始化子类触发父类初始化
    (4)JVM执行main方法会触发main所在的类初始化
  • 被动引用(不会触发类初始化)
    (1)子类引用父类静态字段,不会触发子类初始化
System.out.println(SubClass.value);  // value 字段在 SuperClass 中定义

(2)数组定义引用类,不会触发类初始化,只会触发数组类的初始化,数组类继承自Object

SuperClass[] sca = new SuperClass[10];

(3)使用常量不会触发类初始化

System.out.println(ConstClass.HELLOWORLD);

4. 对象

4.1 对象的创建

类加载检查——>内存分配——>初始化零值——>设置对象头——>执行init方法

  • 类加载检查
    根据符号引用检查类是否被加载过,如果没有,加载类。
  • 分配内存
    在堆中为对象分配空间。分配方法:指针碰撞、空闲列表。
    使用哪种方式,根据java堆是否连续决定。堆是否连续与GC算法相关。
    指针碰撞:堆内存规整(Serial、ParNew)时,用过的和没用过的以指针分界,分界值指针将指针移动对象大小的内存即可。
    空闲列表:堆内存不规整(CMS),虚拟机维护一个内存列表,分配内存,更新列表。
    分配内存可能导致线程安全问题,2种方式解决:CAS+失败重试、TLAB(为每个线程在Eden区分配内存)
  • 初始化零值
    对象的实例字段初始化
  • 设置对象头
    设置对象相关信息:
    (1)对象和类的关系
    (2)如何找到类的元数据信息
    (3)对象的哈希值
    (4)对象的GC年龄
  • 执行init方法
    按照程序猿设置的值初始化。

    4.2 对象的内存布局

    对象头、实例数据、对齐填充
  • 对象头:对象的自身运行时数据(哈希码、GC分代年龄、锁状态等)、类型指针
  • 实例数据:各种类型的字段内容
  • 对齐填充:仅仅占位,保证整个对象对齐,8字节的整数倍。

    4.3 对象的访问

    句柄、直接指针
  • 句柄:
    句柄池(堆):到对象实例数据的指针(指向对象实例数据(堆))、到对象类型数据的指针(指向对象类型数据(方法区))
    好处:对象被移动时,只需要改变句柄中的实例数据指针。
  • 直接指针(堆):到对象类型数据的指针(指向对象类型数据(方法区))、对象实例数据(堆)
    好处:少了一次指针定位的时间开销。

おすすめ

転載: www.cnblogs.com/suyeSean/p/11241906.html