《深刻理解Java虚拟机》(二)Java虚拟机运行时数据区

Java虚拟机运行时数据区 详解

2.1 概述

本文参考的是周志明的 《深刻理解Java虚拟机》第二章 ,为了整理思路,简单记录一下,方便后期查阅。html

2.2 运行时数据区域

Java虚拟机在Java程序运行时会将内存区域划分红若干个不一样的区域,各自负责不一样的职责,这些区域都有各自的用途。
  1. Java虚拟机运行时数据区分为如下几个部分。
  2. 方法区、虚拟机栈、本地方法栈、堆、程序计数器,以下图所示:
图片来源于网络若有侵权请私信删除

图片描述

2.2.1 程序计数器

程序计数器是一块较小的内存空间,能够看做当前线程所执行的字节码行号指示器。须要注意如下几点内容:java

  1. 程序计数器是线程私有,各线程之间互不影响。
  2. 在任何一个肯定的时刻,一个处理器都只会执行一条线程中的指令。
  3. 若是正在执行java方法,计数器记录的是正在执行的虚拟机字节码指令地址。
  4. 若是是native方法,则计数器值为空(native 方法 指得就是Java程序调用了非Java代码,算是一种引入其它语言程序的接口)。
  5. 程序计数器也是在Java虚拟机规范中惟一没有规定任何OutOfMemoryError异常状况的区域。

2.2.2 java虚拟机栈

  • 可经过参数 栈帧是方法运行期的基础数据结构栈容量可由-Xss设置
  1. Java虚拟机栈是线程私有的,它的生命周期与线程相同。
  2. 每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  3. 虚拟机栈是执行Java方法的内存模型(也就是字节码)服务:每一个方法在执行的同时都会建立一个栈帧,用于存储 局部变量表操做数栈动态连接方法出口等信息。
  • 局部变量表:32位变量槽,存放了编译期可知的各类基本数据类型、对象引用、returnAddress类型。
  • 操做数栈:基于栈的执行引擎,虚拟机把操做数栈做为它的工做区,大多数指令都要从这里弹出数据、执行运算,而后把结果压回操做数栈。
  • 动态链接:每一个栈帧都包含一个指向运行时常量池(方法区的一部分)中该栈帧所属方法的引用。持有这个引用是为了支持方法调用过程当中的动态链接。Class文件的常量池中有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用一部分会在类加载阶段或第一次使用的时候转化为直接引用,这种转化称为静态解析。另外一部分将在每一次的运行期间转化为直接应用,这部分称为动态链接
  • 方法出口:返回方法被调用的位置,恢复上层方法的局部变量和操做数栈,若是无返回值,则把它压入调用者的操做数栈。
  1. 局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法须要在帧中分配多大的局部变量空间是彻底肯定的。
  2. 在方法运行期间不会改变局部变量表的大小。主要存放了编译期可知的各类基本数据类型、对象引用 (reference类型)、returnAddress类型)

java虚拟机栈,规定了两种异常情况:

  1. 若是线程请求的深度大于虚拟机所容许的深度,将抛出StackOverflowError异常。
  2. 若是虚拟机栈动态扩展,而扩展时没法申请到足够的内存,就会抛出OutOfMemoryError异常。

2.2.3 本地方法栈

  • 可经过参数 栈容量可由-Xss设置
  1. 虚拟机栈为虚拟机执行Java方法(也就是字节码)服务。
  2. 本地方法栈则是为虚拟机使用到的Native方法服务。有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

2.2.4 java堆

  • 可经过参数 -Xms 初始堆大小-Xmx 最大堆大小-Xmn 新生代` 设置
  1. Java堆是被全部线程共享,是Java虚拟机所管理的内存中最大的一块 Java堆在虚拟机启动时建立。
  2. Java堆惟一的目的是存放对象实例,几乎全部的对象实例和数组都在这里。
  3. Java堆为了便于更好的回收和分配内存,能够细分为,新生代和老年代segmentfault

    **再细致一点的有Eden空间、From Survivor空间、To Survivor区**。
    • 新生代:包括Eden区、From Survivor区、To Survivor区,系统默认大小Eden:Survivor=8:1:1。
    • 老年代:在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代中。所以,能够认为老年代中存放的都是一些生命周期较长的对象。
  4. Survivor空间等Java堆能够处在物理上不连续的内存空间中,只要逻辑上是连续的便可(就像咱们的磁盘空间同样。在实现时,既能够实现成固定大小的,也能够是可扩展的)。
  • 据Java虚拟机规范的规定,当方法区没法知足内存分配需求时,将抛出OutOfMemoryError异常。

2.2.5 方法区

  • 可经过参数-XX:MaxPermSize设置
  1. 线程共享内存区域,用于储存已被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码,方法区也称持久代(Permanent Generation)。
  2. 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,可是它却有一个别名叫作Non-Heap(非堆),目的应该是与Java堆区分开来。
  3. 如何实现方法区,属于虚拟机的实现细节,不受虚拟机规范约束。
  4. 方法区主要存放java类定义信息,与垃圾回收关系不大,方法区能够选择不实现垃圾回收,但不是没有垃圾回收。
  5. 方法区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
  6. 运行时常量池,也是方法区的一部分,虚拟机加载Class后把常量池中的数据放入运行时常量池。

2.2.6 运行时常量池

  • 可经过参数-XX:PermSize-XX:MaxPermSize设置
  • 常量池(Constant Pool):常量池数据编译期被肯定,是Class文件中的一部分。存储了类、方法、接口等中的常量,固然也包括字符串常量。
  • 字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。
  • 运行时常量池(Runtime Constant Pool):方法区的一部分,全部线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。常量池:能够理解为Class文件之中的资源仓库,它是Class文件结构中与其余项目资源关联最多的数据类型。
  1. 常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。
  2. 字面量:文本字符串、声明为final的常量值等;。
  3. 符号引用:类和接口的彻底限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符。
  • JDK1.6以前字符串常量池位于方法区之中
  • JDK1.7字符串常量池已经被挪到堆之中

2.2.7 直接内存

  • 可经过-XX:MaxDirectMemorySize指定,若是不指定,则默认与Java堆的最大值(-Xmx指定)同样
  • 直接内存(Direct Memory)并非虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,可是这部份内存也被频繁地使用,并且也可能致使OutOfMemoryError异常出现。

2.3 hotspot虚拟机对象探秘

2.3.1 对象的建立

  • 主要探讨HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程
  • 虚拟机遇到new指令时
  1. 首先去检查这个指令的参数可否在常量池中定位到一个类的符号引用,而且检查引用表明的类是否已被加载、解析和初始化过。若是没有,则执行类加载过程(第7章 虚拟机类加载机制)。
  2. 加载检查经过后,分配内存(内存在类加载完成后即可彻底肯定)。
  3. 内存分配完成后,虚拟机对对象进行必要的设置,如对象是哪一个类的实例、如何找到类的元数据信息等(都放在对象的对象头中)。
  4. 从虚拟机角度看,一个新的对象产生了,但从java程序视角看,对象建立才刚刚开始,由于<init>方法尚未执行,,全部字段为零。执行new指令以后会接着执行<init>方法(构造方法),进行初始化,这样一个真正可用的对象才算完成产生。

2.3.2 对象的内存布局

对象在内存中存储的布局能够分为3块区域:对象头、实例数据、对齐填充数组

对象头包含两部分(Header)网络

  • 存储对象自身的 运行时数据,如哈希码、GC分代年龄等。长度在32位和64位的虚拟机中,分别为32bit、 64bit,官方称它为“Mark Word”。
  • 类型指针,对象指向它的类元数据的指针,虚拟机经过这个指针来肯定这个对象是哪一个类的实例。

注:若是对象是一个java数组,对象头中还必须有一块记录数据长度的数据数据结构

实例数据(InstanceData)jvm

  • 对象真正存储的有用信息,也是程序中定义的各类类型的字段内容。

对齐填充(Padding)工具

  • 因为HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍,通俗的说,就是对象大小必须是8字节的整数倍。对象头正好是8字节的倍数。当实例数据部分没有对齐时,须要经过对齐填充来补全。

2.3.3 对象的访问定位

  1. Java程序经过栈上的reference数据来操做堆上的具体对象。
  2. 不一样虚拟机实现的对象访问方式会有所不一样,目前主流的访问方式有两种:使用句柄和直接指针。
  3. 使用句柄 是间接访问,优势是reference中存储的是稳定的句柄地址,对象移动时只会改变句柄中的实例数据指针。
  4. 使用直接指针 是直接访问,优势就是速度快。

最后上一张本章结构图

图片来源于网络若有侵权请私信删除

图片描述

《深刻理解Java虚拟机:JVM高级特性与最佳实践_周志明.高清扫描版.pdf》布局

下载地址:连接:http://pan.baidu.com/s/1miBQCBY 密码:9kbn性能

推荐阅读

《深刻理解Java虚拟机》(一)Java虚拟机发展史

《深刻理解Java虚拟机》(二)Java虚拟机运行时数据区

《深刻理解Java虚拟机》(三)垃圾收集器与内存分配策略

《深刻理解Java虚拟机》(四)虚拟机性能监控与故障处理工具

《深刻理解Java虚拟机》(五)JVM调优 - 工具

《深刻理解Java虚拟机》(六)堆内存使用分析,GC 日志解读

Contact

  • 做者:鹏磊
  • 出处:http://www.ymq.io
  • Email:admin@souyunku.com
  • 版权归做者全部,转载请注明出处
  • Wechat:关注公众号,搜云库,专一于开发技术的研究与知识分享

关注公众号-搜云库

相关文章
相关标签/搜索