シリーズJVM(Javaのメモリ領域およびオブジェクトの作成)。

A、JVMのメモリ領域

ヒープ - ヒープ

スレッドシェアは、JVMメモリの最大部分は、このメモリの唯一の目的は、オブジェクト・インスタンスを格納することで、Javaヒープは、ガベージコレクタによって管理されるメイン領域であり、従って、しばしば「GCヒープ」(ガベージヒープ収集)と呼ばれる、によって-Xms -Xmx及び領域の大きさを制御するパラメータ。

メソッド領域 - メソッドエリア

仮想マシンを格納するために使用されるスレッド共有、クラス情報(バージョン、フィールド、メソッド、およびインターフェース記述情報)は、コードコンパイラとして、定数、静的変数、リアルタイムデータをロードされています。

JDK 1.7において、方法は、非ヒープ(非スタック)として知られている領域ロジック部のヒープ領域(ヒープ)に記載されている、HotSpot仮想マシンは、方法を実装するために1.7の領域で生成(Permanent世代)を不死化ガベージコレクタは、メモリ管理の一環として、このようなJavaヒープを管理することができるので、作業は、メソッド領域のために特別に書かれたメモリ管理コードを省くことができ、したがって、しばしば地区と同等の方法の代わりに不死化され、したがって、永遠の生成パラメータ( - XX:PermSizeを、-XX:MaxPermSizeを)メソッドは、メモリ領域のサイズを制限します。

(:PermSizeを、-XX:MaxPermSizeをダンプしたパラメータ-XX)、素子間隔(メタスペース)ボードJDK 1.8において、オーバーフロー法とホットスポットとのJRockitのその後の合併のメモリ領域を削減するために、ホットスポットが永久的生成をキャンセル舞台、メソッド領域は、同時に、元とヒープスペースはもはや連続しているが、ローカルメモリ(ネイティブメモリ)に存在する、要素の空間に存在していない、この手段は十分な長さのローカルメモリなどとして、それは永久的な世代「のjava.langのように表示されていないこと.OutOfMemoryError:デフォルトの道路空間の無制限の使用、ローカルメモリによってPermGen空間「このエラー、元のスペース(:MaxMetaspaceSize:MetaspaceSize、-XX -XX)の大きさを制限することもできます。

ランタイム定数プール - ランタイム定数プール

直接参照のうち、コンテンツの共有、ファイルストレージ、クラスの定数プール(コンパイルにクラスの後の部分)を含む、翻訳を通します。

クラス定数プールの内容は次のとおりです。

ランタイム定数プール、Java仮想マシン仕様の仮想マシンの異なるプロバイダのいずれかの詳細は、このメモリ領域を達成するために、自分のニーズに合わせて実現することができます必要はありません。クラスの定数プールへのランタイム定数プールファイルの相対的な重要な特徴は、新しい定数を配置することも可能である動作時に、それは一定のプールファイルのコンテンツゾーンのランタイム定数プールを入力するには、クラスメソッドにプリセットされていない、ダイナミックに装備されていプールに、インターン文字列クラス()メソッドなどのより一般的なようにします。

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

同じスレッドでプライベートライフサイクルを通し、説明のJavaメモリモデルを実行する方法であって、各メソッドの同時実行は、輸出のためのローカル変数テーブルを格納するためのスタックフレーム(スタック・フレーム)、オペランドスタック、動的リンクを作成します。その他の情報。プロセスが完了するまで実行から各メソッド呼び出しは、それがプロセス・スタックのスタックフレームへのプッシュに相当します。

バイトコードの実行を指す知ら(ブール、バイト、文字、ショート、整数、フロート、長い、二重)、オブジェクト参照(参照型)の様々なコンパイラの基本的なタイプ、RETURNADDRESSタイプ(アドレスに格納されたローカル変数テーブル)。前記64ビットのデータのロングとダブル長さは、2つのローカル変数空間(スロット)を占有します。ローカル変数テーブルのサイズを変更しない方法の操作中に、コンパイル時に割り当てられたローカル変数テーブルのメモリ空間の完了に必要。

VMスタックおよびネイティブメソッドスタックの違いは、しかし、Java仮想マシンサービスメソッドのための仮想マシンの実行スタックであり、仮想マシンのネイティブメソッドスタックはネイティブメソッドのサービスです。1つのスタックに仮想マシンとネイティブメソッドスタックに直接仮想マシンをホットスポット。仮想マシン-Xssスタックサイズパラメータによって提供されてもよい、-Xossネイティブメソッドスタックパラメータセット(パラメータのHotSpot VMに反映されません)。

プログラムカウンタ

プライベートスレッド、小さなメモリ空間、それが現在の実行スレッドのインジケータバイトコードの行番号として見ることができ、このメモリ領域は1例のみであるため、Java仮想マシン仕様の任意のエリアのOutOfMemoryErrorのために提供されていません。領域は、少なくとも関心プログラマのエリアとなっています。

直接内存 - Direct Memory

线程私有,并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。Java NIO (New Input/Output)是一种基于通道(Channel)与缓存区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

该区域也可能导致内存溢出,一个明显的特征是在 Heap Dump 文件中不会看见明显的异常。因此,服务器管理员在根据实际内存配置虚拟机参数时,需要考虑到直接内存需要的空间,可以通过 -XX:MaxDirectMemorySize 来指定直接内存的大小,如果不指定,则默认与 Java 堆的最大值(-Xmx)一样。

二、Java 对象创建

接下来看看我们平常的一个 new 操作在 JVM 中又是怎样一种过程呢?(讨论的是普通 Java 对象,不包括数组和 Class 对象等)。

1. 栈空间分配

当执行 new 操作的时候,首先进行的是在Java 栈的局部变量表中分配一个对象引用(reference 类型,不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄)。

2. 类加载检查

JVM 检查这个对象是否能在常量池(指的是 Class 文件常量池)中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那必须先执行类加载过程(静态块、静态变量、静态方法加载进静态方法区等操作)。

3. 分配内存

对象所需的内存大小在类加载完成后便可完全确定,因此为对象分配内存空间其实就是怎样把一块确定大小的内存从 Java 堆中划分出来。一般有两种分配方式:

指针碰撞
Java 堆中的内存是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。

空闲列表
Java 堆中的内存并不是规整的,虚拟机维护了一个列表,记录了哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

内存分配的方式由 Java 堆是否规整决定, Java 堆是否规整又是由所采用的垃圾收集器是否带有 compact(压缩整理)功能决定。比如 Serial、ParNew 等基于 stop-and-copy 算法的收集器就具有 compact 功能,而 CMS 这种基于 mark-and-sweep 算法的收集器就不具有 compact 功能。

虚拟机默认使用 CAS 配上失败重试的方式保证内存分配操作的原子性,可通过 -XX:+/-UseTLAB 指定使用 TLAB(Thread Local Allocation Buffer, 本地线程分配缓冲);

HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,换句话说,就是对象的大小必须是 8 字节的整数倍。因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

4. 初始化工作

接下来虚拟机加载非静态块、非静态方法、非静态变量,并将分配到的内存空间都初始化零值(引用类型初始化为 null,int 类型初始化为 0 等),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就能直接使用。

5. 对象头设置

接下来虚拟机将进行对象头的填充设置,HotSpot 虚拟机的对象头包括一般两部分信息:

第一部分(Mark Word)
存储对象自身的运行时数据,如哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在 32 位和 64 位虚拟机(未开启压缩指针)中分别为 32bit 和 64 bit。

第二部分(类型指针)
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。但是并不是所有的虚拟机实现都必须在对象数据上保留类型指针,比如通过句柄访问。下文会提到。

如果对象是一个数组,那么对象头中还必须有一块用于记录数组长度的数据,因为虚拟机从数组的元数据中无法确定数组的大小。

6.构造器工作

如果有父类,则父类按上述流程保证被加载。

7. 对象的访问定位

现在堆中的对象实例有了,栈中的 reference 也有了,怎么将两者关联在一起呢?目前主流的方式有使用句柄和直接指针两种:

使用句柄
Java 堆中划分出一块内存作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象的实例数据与类型数据各自的具体地址信息。它的优点就是 reference 存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。

直接指针
reference 中存储的直接就是对象地址。它的好处就是速度更快,节省了一次指针定位的时间开销。


HotSpot VM 使用的直接指针进行对象访问。

おすすめ

転載: www.cnblogs.com/jmcui/p/11875858.html