第二章 Java内存区域与内存溢出异常
2.2 运行时数据区域
 
 
Java虚拟机在执行java程序的过程中会把它所管理的内存划分为不同的数据区。
 
2.2.1 程序计数器
  1. 程序计数器可以看作当前线程所执行的字节码的行号指示器。在虚拟机概念模型里,字节码解释器的工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
  2. 如果线程正在执行java方法,这个计数器记录的就是正在执行的虚拟机字节码指令的地址;如果正在执行的时Native方法,这个计数器的值为空。
  3. 此内存区域是唯一一个在java虚拟机规范中没有规定任何一个OutOfMemoryError情况的区域。
  4. 每个线程私有。
 
2.2.2 java虚拟机栈
  1. 虚拟机栈也是线程私有的,生命周期与线程相同。
  2. 虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程。
  3. 平时所说的“栈”即为虚拟机栈或虚拟机栈中的局部变量表,局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)。
  4. 局部变量表所需的内存空间在编译期间完成分配,当进入一个方法的时候,这个方法所需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
  5. 这个区域有两种异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果虚拟机可以动态扩展,扩展时无法申请到足够的内存,抛出OutOfMemoryError异常。
 
2.2.3 本地方法栈
    和虚拟机栈类似,区别在于虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务。也会抛出StackOverflowError异常、OutOfMemoryError异常。
 
2.2.4 Java堆
  1. 对于大多数应用来说,Java堆(java heap)是java虚拟机所管理的内存中最大的一块。java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
  2. 此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。java虚拟机规范中描述是:所有对象实例及数组都要在堆上分配内存。
  3. java堆是垃圾收集器管理的主要区域。
  4. java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
 
2.2.5 方法区
    各线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
 
2.2.6 运行时常量池
    Runtime Constant Pool ,是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。
 
    运行时常量池相对于class文件常量池的另外一个重要特征是具备动态性,java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,例如String的intern()方法。
 
2.2.7 直接内存
Direct Memory,JDK 1.4中加入了NIO类,引入了一种基于通道(channel)与缓冲区(buffer)的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在java堆和native堆中来回复制数据。
 
 
 
2.3 HotSpot虚拟机对象探秘
2.3.1 对象的创建
       虚拟机遇到一个new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,则必须先执行对应类的加载过程。当类加载检测通过后,虚拟机为新生成的对象分配内存。对象需要的内存在类加载后便可完全确认。分配内存任务等同于把一块确定大小的内存从Java堆中划分出来。
内存分配方式由java堆是否规整决定,而java堆是否规整由垃圾收集器是否带有压缩功能决定。(1)“指针碰撞”:假设java堆中的内存是绝对规整的,所有空闲内存放在一起,使用过的内存放在一起,用一个指针隔离开,每次分配内存指针做部分偏移。(2)“空闲列表”:java堆中的内存不是规整的,虚拟机维护一个列表记录内存块的使用情况。
     线程安全问题,解决方法:(1)对分配内存的活动进行同步处理---cas+失败重试(2)每个线程在java堆中预先分配一小块内存,称为 Thread Local Allocation Buffer, TLAB。TLAB使用完后分配新TLAB才需要同步锁定。
    内存分配完成后,虚拟机将分配到的内存空间都初始化为零值(对象头除外)。接下来对对象的对象头进行必要设置。 然后执行<init>方法。
    
2.3.2 对象的内存布局
    在hotspot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding).
对象头包括两部分:用于存储对象自身的运行时数据,称为“Mark Word”。另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身。此外,如果对象是一个java数组,对象头中还必须要有一段用于记录数组长度的数据。
 
    对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。32位虚拟机上,在对象不同状态时 mark word各个比特位区间的含义。
 
    实例数据部分:存储代码中定义的字段内容。父类以及子类中定义的都需要存储,而存储顺序受到虚拟机分配策略参数和字段在java源码中定义顺序影响。
    对齐填充:HotSpot VM 的自动内存管理系统要求对象起始地址必须是8字节的倍数,也就是说对象的大小必须是8字节的倍数,当实例数据部分没有对齐时,需要对齐填充来补全。
 
2.3.3 对象的定位
    java程序需要通过栈上的reference数据来操作堆上的对象。访问堆上对象主流有两种方式:
(1)使用句柄访问:在java堆中划分一块内存来存储句柄池,reference中存储的就是对象的句柄地址,而句柄地址中包含了对象实例数据和类型数据(static)各自的具体地址信息。
(2)直接指针访问 :reference中存储的就是对象地址。

《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常的更多相关文章

  1. <<深入Java虚拟机>>-第二章-Java内存区域-学习笔记

    Java运行时内存区域 Java虚拟机在运行Java程序的时候会将它所管理的内存区域划分为多个不同的区域.每个区域都有自己的用途,创建以及销毁的时间.有的随着虚拟机的启动而存在,有的则是依赖用户线程来 ...

  2. 深入理解java虚拟机-第二章:java内存区域与内存泄露异常

    2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...

  3. 虚拟机--第二章java内存区域与内存溢出异常--(抄书)

    这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...

  4. 深入理解java虚拟机-第二章

    第2章 Java内存区域与内存溢出异常 运行数据区域 1.程序计数器(Program Counter Register) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 2.J ...

  5. JAVA基础第二章-java三大特性:封装、继承、多态

    业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...

  6. java入门第二章——java编程基础

    习题 一.填空题 (p)1.java中的程序代码都必须在一个类中定义,类使用(class)关键字来定义. (p)2.布尔常量即布尔类型的两个值,分别是(true)和(false) (p18)3.jav ...

  7. 《深入理解Java虚拟机》笔记--第二章、Java内存区域与内存溢出异常

    Java程序员把内存的控制权交给了Java虚拟机.在Java虚拟机内存管理机制的帮助下,程序员不再需要为每一个new操作写对应的delete/free代码,而且不容易出现内存泄露和溢出. 虚拟机在执行 ...

  8. 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常

    2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...

  9. 深入学习重点分析java基础---第一章:深入理解jvm(java虚拟机) 第一节 java内存模型及gc策略

    身为一个java程序员如果只会使用而不知原理称其为初级java程序员,知晓原理而升中级.融会贯通则为高级 作为有一个有技术追求的人,应当利用业余时间及零碎时间了解原理 近期在看深入理解java虚拟机 ...

随机推荐

  1. python模块_re模块

    正则表达式笔记'''#re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none.#re.search 扫描整个字符串并返回第一个成功的匹配#fi ...

  2. Linux基础学习(2)--Linux系统安装

    第二章——Linux系统安装 一.VMware虚拟机安装与使用 1.VMware简介:            VMware是一个虚拟PC的软件,可以在现有的操作系统上虚拟出一个新的硬件环境,相当于模拟 ...

  3. Visual Studio Code之常备快捷键

    官方快捷键大全:https://code.visualstudio.com/docs/customization/keybindings Visual Studio Code是个牛逼的编辑器,启动非常 ...

  4. MySQL中char、varchar和nvarchar的区别

    一.char和varchar的区别char是固定长度的,而varchar会根据具体的长度来使用存储空间,另外varchar需要用额外的1-2个字节存储字符串长度.1). 当字符串长度小于255时,用额 ...

  5. BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案

    题目链接: CTSC2018混合果汁 显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案. 那么我们二分一个美味度作为答案然后考虑如何验证? 选择时显然要贪心的 ...

  6. HDU-2087-KMP-水题

    纯KMP #include <cstdio> #include <algorithm> #include <cstring> #include <ctype. ...

  7. log4net 单独项目

    首先参考:http://blog.csdn.net/feiying008/article/details/45440547 有时,我们需要将日志功能作为单独模块,用来以后嫁接到其他项目. 今天就来看看 ...

  8. 详解HTTP协议

    一 什么是HTTP协议 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网服务器传输超文本到本地浏览器的传送协议.HTTP 是基于 TCP/ ...

  9. 自学Linux Shell16.3-函数递归+创建库

    点击返回 自学Linux命令行与Shell脚本之路 16.3-函数递归+创建库 1. 函数递归 递归调用函数是指函数调用自身进行求解. 通常,递归函数有基值,函数最终递推到达该值. 许多高级数学算法使 ...

  10. 【BZOJ2142】礼物(拓展卢卡斯定理)

    [BZOJ2142]礼物(拓展卢卡斯定理) 题面 BZOJ 洛谷 题解 显然如果\(\sum w_i>n\)无解. 否则答案就是:\(\displaystyle \prod_{i=1}^m{n- ...