第二章

Java内存区域与内存溢出异常

一、概述

对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问

题,由虚拟机管理这一切看起来很美好。但是一旦出现内存泄露和内存溢出问题,如果不了解虚拟机是怎么使用内存的,那么排查错误将会成为

一项异常艰难的工作。

二、运行时数据区域

JVM所管理的内存将会包括以下几个运行时数据区域

  1. 程序计数器

定义:

程序计数器是一块较小的内存空间,它可以看作是当前线程执行的字节码的行号指示器。

进一步了解:

1在虚拟机概念模型里(仅是概念模型,各种虚拟机可能会通过更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要这个功能要依赖这个计数器来完成。

2.多线程执行时,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存。

3.如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器则为空。

溢出:

此内存区域是Java虚拟机规范中唯一没有规定任何OutMemoryError情况的区域

  1. Java虚拟机栈

定义:

虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

额外的:

1.每个方法调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中。线程私有的,它的生命周期与线程相同。

2.经常有人把Java内存分为堆内存 和 栈内存,这种分法比较粗糙,Java内存区域的划分实际上远比这复杂 。这种划分方式的流行只能说明程序员最关注的、与对象内存分配关系最密切区域是这两块。其中所指的堆即是后面将写的的Java堆,而所指的"栈"就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表部分。

进一步理解:

局部变量表存放了编译期可知(方法运行期间不会改变局部变量表的大小)的各种基本数据类型、对象引用(后面讲对象的访问定位会具体说) 和 returnAddress类型(指向了一条字节码指令的地址)。

溢出:

  1. 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
  2. 如果虚拟机栈可以动态扩展(大部分VM都可以),扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

(3)本地方法栈

定义/区别:

本地方法栈与虚拟机栈所发挥的作用非常相似,它们间的区别不过是虚拟机栈执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

额外的:

        VM规范没有没有强制规定,因此虚拟机可以自由实现它,甚至有的虚拟机(如 HotSpot VM)直接就把本地方法栈和虚拟机栈合二为一

溢出:

同Java虚拟机栈。

(4)Java堆

定义:

Java虚拟机管理的内存最大的一块,是被所有线程共享的,在虚拟机启动时创建。唯一的目的就是存放对象实例。

额外的:

几乎所有对象实例都在堆上分配内存,但随着JIT的发展与逃逸分析技术逐渐成熟,渐渐所有对象都分配堆上变得不那么"绝对"了。

进一步:

Java堆是垃圾收集器管理的主要区域,因此很多时候也称"GC堆"。从内存回收角度看,由于现在收集器基本都采用分代收集算法(分代算法把 jdk1.7及以前分代 分为 新生代、老年代、永久代,jdk1.8开始把 永久代 移除),而刚好就是Java堆被分为 新生代 和 老年代,再细致一点有Eden空间、From Survivor空间、To Survior空间等。而从内存分配角度,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。

溢出:

如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

(5)方法区

定义:

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

额外的:

虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但它却有一个别名叫 None-Heap(非堆),目的应该是与Java堆区分开来。

进一步:

对于习惯在HotSpot虚拟机上开发、部署的开发者来说,很多人愿意把方法区称为"永久代",仅仅是因为HotSpot使用永久代实现了方法区,因为hotspot的垃圾收集器采用GC分代算法,并把分代范围扩展到了方法区(给方法区分为"永久代"),可以节省单独写方法区的内存管理代码。

然而,现在看来不是好主意,这样会更容易遇到内存溢出,因此jdk1.7中已经把"永久代"中的字符串常量池移出了,然后在jdk1.8中甚至直接移除了"永久代",改用元空间实现方法区且元空间不在虚拟机了而是使用本地内存。

回收:

这区域内存回收目标主要是针对常量池的回收和对类型的卸载。

溢出:

如果无法满足内存分配需求时,将会抛出OutOfMemoryError异常。

三、运行时数据区域附带的讲解

(1)运行时常量池

定义/位置:

运行时常量池 是 方法区的一部分。

额外的:

Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

进一步:

运行时常量池相对于Class文件常量池的区别:

  1. Class文件常量池得符合Java虚拟机规范(Class文件每一部分都严格要求,当然也包括常量池)。运行时常量池则不作要求,由具体VM实现。
  2. 运行时常量池具备动态性。Java语言不要求常量一定只有编译期产生(也就是并非预置入Class文件中常量池的内容才能进入运行时常量池),运行期间也可以将常量加入运行时常量池。比如String的intern()方法

溢出:

运行时常量池 是 方法区的一部分。同样,如果无法满足内存分配需求时,将会抛出OutOfMemoryError异常。

(2)直接内存(堆外内存)

介绍:

直接内存 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是也经常使用,也会发生OutOfMemoryError,所有一起在这讲。

进一步:

Jdk1.4中加入了NIO,引入了一种基于通道(channle)与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提升性能,因为避免了在Java堆和Native堆中来回复制数据。

溢出:

还是受到本级总内存大小以及处理器寻址空间的限制。当各个内存区域总和大于物理内存限制从而导致动态扩展时出现OutOfMemoryError异常。

四、HotSpot VM在Java堆中的对象分配、布局和访问

(1)对象的创建(限于普通Java类,不包括数组和Class对象)

首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号所代表的类是否已经被加载、解析、和初始化过。如果没有,那必须先执行相应的类加载过程(《深入理解Java虚拟机》第七章将会讨论这个过程)

类加载检查通过后,接下来虚拟机将会为新生对象分配内存。对象所需的内存的大小在类加载之后就可完全确定(如何确定在《深入理解Java虚拟机》2.3.2节介绍)。

第二章Java内存区域与内存溢出异常的更多相关文章

  1. 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常

    第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域  

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

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

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

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

  4. 深入理解java虚拟机系列(一):java内存区域与内存溢出异常

    文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...

  5. 2.1 自动内存管理机制--Java内存区域与内存溢出异常

    自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存 ...

  6. 深入理解java虚拟机---->java内存区域与内存溢出异常

    2. java内存区域于内存溢出异常 2.1 概述: 对于C/C++而言,内存管理具有最高的权利,既拥有每一个对象的“所有权”,又担负着每一个对象生命开始到结束的维护责任. 对于java而言,则把内存 ...

  7. 深入理解Java虚拟机之Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  8. 深入理解Java虚拟机之图解Java内存区域与内存溢出异常

    Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...

  9. 深入了解Java虚拟机(1)java内存区域与内存溢出异常

    java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有,用于存储当前所执行的指令位置 2.Java虚拟机栈:线程私有,描叙Java方法执行模型:执行方法时都会创建一个栈帧,存储局 ...

随机推荐

  1. 使用Idea第一次创建一个Mavne工程时没有src目录

    在使用idea创建一个maven工程时没有src目录,可能出现的问题很多,我先把我自己的问题分享上来 因为没有src,可能是因为maven插件还没下载到本地仓库.maven插件的版本和jdk版本冲突或 ...

  2. java树形结构工具类

    一.树形结构数据一般都是以子父id的形式存在数据库中,查询的时候只是带有子id和parent_id的List集合 并不是树形结构,所以我们现在要将普通的List集合转换为树结构数据(本工具类扩展操作树 ...

  3. python接口自动化测试七:获取登录的Cookies

    python接口自动化测试七:获取登录的Cookies,并关联到下一个请求   获取登录的cookies:loginCookies = r.cookies 把获取到的cookies传入请求:cooki ...

  4. 网站启动,报编译错误:类型“ASP.global_asax”同时存在两个文件夹的问题

    CS0433: The type 'ASP.global_asax' exists in both 'c:\Windows\Microsoft.NET\Framework64\v4.0.30319\T ...

  5. [Pandas] 01 - A guy based on NumPy

    主要搞明白NumPy“为什么快”. 学习资源 Panda 中文 易百教程 远程登录Jupyter笔记本 效率进化 四步效率优化 NumPy 底层进行了不错的优化. %timeit 对于任意语句,它会自 ...

  6. docker的使用---创建新的镜像(通过修改容器,个人练手理解过程记录,不推荐使用)

    docker基础命令 ##列出docker客户端命令 docker docker container --help ##显示docker的版本和信息 docker --version docker v ...

  7. springboot之全局处理统一返回

    springboot之全局处理统一返回 简介 在REST风格的开发中,避免通常会告知前台返回是否成功以及状态码等信息.这里我们通常返回的时候做一次util的包装处理工作,如:Result类似的类,里面 ...

  8. npm install 时间很长解决方案

    国外镜像站很慢,所以我们可以更换为国内的镜像站 首先可以get命令查看registry npm congfig get registry 如果你没有变更果regustry你的结果应该会是这样的 也就是 ...

  9. ppt课件动手动脑实际验证

    1关于double精度 源代码:public class Doublejingdu { public static void main(String[] args) { System.out.prin ...

  10. java -PDF添加文本水印与图片水印

    java pdf添加水印文本及图片文本 PDF文件添加文本水印: private static int interval = 30; public static void waterMark(Stri ...