摘要: JVM内存的划分,导致内存溢出异常的可能区域。

1. JVM运行时内存区域

  JVM在执行Java程序的过程中会把它所管理的内存划分为以下几个区域:

1.1 程序计数器

  程序计数器是一块较小的内存空间,在Java虚拟机规范中是唯一一个未规定OutOfMemoryError的内存区域。

  程序计数器可看作当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变计数器的值来选取下一条待执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器来完成。

  每个线程均有一个独立的程序计数器。若线程执行的是一个Java方法,计数器记录的值是正在执行虚拟机字节码地址;若执行的是Native方法,计数器值为空(Undefined)。

1.2 Java虚拟机栈

  Java虚拟机栈也就是我们经常说的"栈"(其实这个说法不够严谨<^_^>),它是线程私有的,生命周期与线程相同。

  虚拟机栈描述的是Java方法执行的内存模型:每个方法执行时都会创建一个栈帧,用于存储局部变量栈、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程,都对应者一个栈帧在虚拟机栈中入栈到出栈的过程。

  局部变量表存放了编译期可知的8种基本数据类型、对象引用(reference类型,指向对象起始地址的引用指针或代表对象的句柄或其他与此对象相关的位置)、returnAddress类型(指向一条字节码指令的地址)。

  Java虚拟机规范对这块区域规定了两种异常情况:

    • StackOverflowError

      线程请求的栈深度大于虚拟机所允许的深度,将抛出此异常;

    • OutOfMemoryError

      若虚拟机栈可以动态扩展,但在扩展时无法申请到足够的内存,将抛出此异常;

1.3 本地方法栈

  与虚拟机栈类似,但它执行的Native方法服务。

1.4 Java

  "GC"堆,虚拟机启动时创建,为所有线程共享的一块区域。在Java虚拟机规范描述中,所有对象实例和数组都要在堆上分配(随着JIT编译器的发展和逃逸技术的逐渐成熟,所有对象在堆上分配也变得不是这么绝对)。

  如果堆中剩余内存不足以完成实例的分配,且无法再扩展,将抛出OutOfMemoryError。

1.5 方法区

  "Non-Heap",存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。

  根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError。

1.6 运行时常量池

  方法区的一部分,在类加载后进入方法区时,存放编译期生成的各种字面量和符号引用。

  在运行期间,也可能将新的常量放入池中,如String常用的intern()方法。

  当无法满足内存分配需求时,将抛出OutOfMemoryError。

1.7 直接内存

  这部分区域并不是虚拟机运行时数据区的一部分,也非Java虚拟机规范中定义的内存区域,但也被频繁使用,亦可能抛出OutOfMemoryError。

  直接内存主要为NIO(JDK1.4 加入的类)使用,通过存储在Java堆中的DirectByteBuffer对象对这块内存的引用进行操作。在某些场合能显著提高性能,避免了Java堆和Native堆中来回复制数据。

2. HotSpot虚拟机对象

  讨论Java堆中普通对象分配、布局和访问的过程。

2.1 对象的创建

    • 虚拟机遇到一条new命令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过,若没有,则先执行相应的类加载过程;
    • 在类加载检查通过后,虚拟机将为新生对象分配内存:
      • 指针碰撞

        若Java堆中的内存是绝对规整的,所有用过的和空闲的内存都各放一边,中间放着一个指针作为分界点的指示器,那分配内存仅仅是将指针向空闲空间方向移动一段与对象大小相等的距离;

    • 空闲列表

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

      并发情况下,在堆上分配内存,可能出现内存分配的同步问题,解决方案有两个,一个就是同步内存(CAS)分配动作;另一个就是采用TLAB(本地线程分配缓冲),即在Java堆中针对每个线程先预先分配一小块内存。这样当线程需要分配内存时就在自己的TLAB上进行,从而避免同步的开销。但是当TLAB分配满重新分配TLAB时仍需要同步。在内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)。

    • 虚拟机将要对对象进行必要的设置,如这个对象是哪个类的实例,如何找到类的元数据信息、对象的hashCode和GC分代年龄等信息。这些信息存放在对象的对象头中。

最后执行init方法,从而产生真正可用的对象。

2.2 对象的内存布局

在HotSpot虚拟机中,对象在内存的存储的布局可分为3块区域:对象头、实例数据、对齐填充。

    • 对象头
      • Mark Word

          存储对象自身的运行时数据,如hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;

      • 类型指针

          对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。

    • 实例数据

      程序代码中所定义的各种类型的字段内容,包括从父类继承下来的。

    • 对齐填充

      并不是必然存在,仅仅起着占位符作用,以满足HotSpot VM 自动内存管理系统对对象起始地址必须是8字节的整数倍要求。

2.3 对象的访问定位

  Java程序通过栈上的reference数据来操作堆上的具体对象。主流的访问方式:

    * 句柄

      Java堆中将划分一块内存用作句柄池,reference中存储的就是对象的句柄地址,而句柄包含了对象实例数据与类型数据各自的具体地址信息。

      优点:当对象移动时,只需要改变句柄的指针即可,而相应的reference则不需要做变动;

  • 直接指针

  reference存储的是对象地址,通过reference就能访问数据。

  优点:访问速度快,相对句柄的方式而言少了一次指针定位的开销。HotSpot VM使用的是此种方式。

  

参考文献

深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)

JVM自动内存管理-Java内存区域与内存溢出异常的更多相关文章

  1. ios内存管理2-对象之间的内存管理

    同之前一样,新建一个基于命令行的工程,在新建一个Student类和一个Book类 编写如下代码: Student.h // // Student.h // 内存管理2-对象之间的内存管理 // // ...

  2. 从内存管理原理,窥探OS内存管理机制

    摘要:本文将从最简单的内存管理原理说起,带大家一起窥探OS的内存管理机制,由此熟悉底层的内存管理机制,写出高效的应用程序. 本文分享自华为云社区<探索OS的内存管理原理>,作者:元闰子 . ...

  3. C++内存管理5-处理new分配内存失败情况(转)

    C++内存管理5-处理new分配内存失败情况(转) endl; 参考博客: https://www.cnblogs.com/findumars/p/9905195.html

  4. JVM内存管理------JAVA语言的内存管理概述

    引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓 ...

  5. JVM学习笔记(一):Java虚拟机和虚拟机内存区域

    为什么Java程序需要运行在虚拟机上 因为Java在设计之初的跨平台特性,我们知道Java程序是运行在Java虚拟机上的.如果你要问为什么Java程序要运行在虚拟机上,我可以反问你几个问题. 为什么买 ...

  6. 你必须了解的java内存管理机制(二)-内存分配

    前言 在上一篇文章中,我们花了较大的篇幅去介绍了JVM的运行时数据区,并且重点介绍了栈区的结构及作用,相关内容请猛戳!在本文中,我们将主要介绍对象的创建过程及在堆中的分配方式. 相关链接(注:文章讲解 ...

  7. 内存管理 垃圾回收 C语言内存分配 垃圾回收3大算法 引用计数3个缺点

    小结: 1.垃圾回收的本质:找到并回收不再被使用的内存空间: 2.标记清除方式和复制收集方式的对比: 3.复制收集方式的局部性优点: https://en.wikipedia.org/wiki/C_( ...

  8. 死磕内存篇 --- JAVA进程和linux内存间的大小关系

    运行个JAVA 用sleep去hold住 package org.hjb.test; public class TestOnly { public static void main(String[] ...

  9. 启动期间的内存管理之build_zonelists初始化备用内存域列表zonelists--Linux内存管理(十三)

    1. 今日内容(第二阶段(二)–初始化备用内存域列表zonelists) 我们之前讲了在memblock完成之后, 内存初始化开始进入第二阶段, 第二阶段是一个漫长的过程, 它执行了一系列复杂的操作, ...

随机推荐

  1. xtrabackup全量备份和增(差)量备份

    xtrabackup全量备份和增(差)量备份 1.xtrabackup全量备份和恢复 1)备份: innobackupex --default-file=/PATH/TO/DEFAULT --host ...

  2. Weblogic新建域,Weblogic新建部署环境,Weblogic重新构建域

    Weblogic新建域,Weblogic新建部署环境,Weblogic重新构建域 ======================== 蕃薯耀 2018年1月29日 http://www.cnblogs. ...

  3. id,is的用法,小数据池的概念及编码知识进阶

    一:id 查询内存地址 name = 'alex' print(id(name)) li = [1,2,3] print(id(li)) 二:is  判断的是内存地址 name1 = 'alex@' ...

  4. js 原型规则与示例

    五大规则 1. 所有的引用类型( 数组 对象 函数 ) 都是 具有对象特性即自由拓展属性 (除了 "null")意外 2. 所有的引用类型(数组 对象 函数 ) 都有一个 prot ...

  5. 2017-07-05 (whereis which find)

    whereis whereis 命令名 作用 搜索命令所在的路径以及帮助文档所在的位置 选项 -b 搜索命令所在的位置 -m 搜索帮助文档所在的位置 例子 whereis ls  查看ls命令所在的位 ...

  6. 【处理多服务器日志合并处理问题】多服务器的日志合并统计——apache日志的cronolog轮循

    转发:http://www.chedong.com/tech/rotate_merge_log.html   内容摘要:你完全不必耐心地看完下面的所有内容,因为结论无非以下2点:1 用 cronolo ...

  7. scss 初学笔记 三 继承

    //继承 .btn{ padding: 4px 10px; font-size: 14px; } .primary{ background:red; @extend .btn; } //%placeh ...

  8. OpenCv结构和内容

    OpenCv的结构和内容 OpenCv源码组成结构其中包括cv, cvauex, cxcore, highgui, ml这5个模块 CV:图像处理和视觉算法 MLL:统计分类器 HighGui:GUI ...

  9. 解决跨站脚本注入,跨站伪造用户请求,sql注入等http安全漏洞

    跨站脚本就是在url上带上恶意的js关键字然后脚本注入了,跨站伪造用户请求就是没有经过登陆,用超链接或者直接url上敲地址进入系统,类似于sql注入这些都是安全漏洞. sql注入 1.参数化查询预处理 ...

  10. 简述 Hibernate 和 JDBC 的区别、优缺点

    1.hibernate是一个开源的.采用面向对象的思想实现ORM映射框架,它对jdbc进行了一层封装,对于数据库的连接.关闭.数据的持久化(增删改查).事务的管理都进行了封装,使得程序开发的时候可以用 ...