第二章Java内存区域与内存溢出异常
第二章
Java内存区域与内存溢出异常
一、概述
对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问
题,由虚拟机管理这一切看起来很美好。但是一旦出现内存泄露和内存溢出问题,如果不了解虚拟机是怎么使用内存的,那么排查错误将会成为
一项异常艰难的工作。
二、运行时数据区域
JVM所管理的内存将会包括以下几个运行时数据区域
程序计数器
定义:
程序计数器是一块较小的内存空间,它可以看作是当前线程执行的字节码的行号指示器。
进一步了解:
1在虚拟机概念模型里(仅是概念模型,各种虚拟机可能会通过更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要这个功能要依赖这个计数器来完成。
2.多线程执行时,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存。
3.如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器则为空。
溢出:
此内存区域是Java虚拟机规范中唯一没有规定任何OutMemoryError情况的区域
Java虚拟机栈
定义:
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
额外的:
1.每个方法调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中。线程私有的,它的生命周期与线程相同。
2.经常有人把Java内存分为堆内存 和 栈内存,这种分法比较粗糙,Java内存区域的划分实际上远比这复杂 。这种划分方式的流行只能说明程序员最关注的、与对象内存分配关系最密切区域是这两块。其中所指的堆即是后面将写的的Java堆,而所指的"栈"就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表部分。
进一步理解:
而局部变量表存放了编译期可知(方法运行期间不会改变局部变量表的大小)的各种基本数据类型、对象引用(后面讲对象的访问定位会具体说) 和 returnAddress类型(指向了一条字节码指令的地址)。
溢出:
- 线程请求的栈深度大于虚拟机所允许的深度,抛出StackOverflowError异常
- 如果虚拟机栈可以动态扩展(大部分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文件常量池的区别:
- Class文件常量池得符合Java虚拟机规范(Class文件每一部分都严格要求,当然也包括常量池)。运行时常量池则不作要求,由具体VM实现。
- 运行时常量池具备动态性。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内存区域与内存溢出异常的更多相关文章
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 虚拟机--第二章java内存区域与内存溢出异常--(抄书)
这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...
- 深入理解java虚拟机-第二章:java内存区域与内存泄露异常
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- 深入理解java虚拟机系列(一):java内存区域与内存溢出异常
文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...
- 2.1 自动内存管理机制--Java内存区域与内存溢出异常
自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存 ...
- 深入理解java虚拟机---->java内存区域与内存溢出异常
2. java内存区域于内存溢出异常 2.1 概述: 对于C/C++而言,内存管理具有最高的权利,既拥有每一个对象的“所有权”,又担负着每一个对象生命开始到结束的维护责任. 对于java而言,则把内存 ...
- 深入理解Java虚拟机之Java内存区域与内存溢出异常
Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...
- 深入理解Java虚拟机之图解Java内存区域与内存溢出异常
Java内存区域与内存溢出异常 运行时数据区域 程序计数器 用于记录从内存执行的下一条指令的地址,线程私有的一小块内存,也是唯一不会报出OOM异常的区域 Java虚拟机栈 Java虚拟机栈(Java ...
- 深入了解Java虚拟机(1)java内存区域与内存溢出异常
java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有,用于存储当前所执行的指令位置 2.Java虚拟机栈:线程私有,描叙Java方法执行模型:执行方法时都会创建一个栈帧,存储局 ...
随机推荐
- JDK8时间工具类
JDK8添加了java.time包,提供了很多方便.用得比较多的几个类:Instant 在时间线上模拟单个瞬时点Duration 以秒和纳秒为单位模拟一个数量或时间量.可以使用其他基于持续时间的单位访 ...
- 固定定位下的div水平居中
发现了一个之前未留意的知识点,做个笔记. 当一个块级元素的父元素开启了flex布局后,我们可以很轻松的将这个元素居中对齐,可以在父元素上加 justify-content: center; align ...
- java架构之路-(源码)mybatis的一二级缓存问题
上次博客我们说了mybatis的基本使用,我们还捎带提到一下Mapper.xml中的select标签的useCache属性,这个就是设置是否存入二级缓存的. 回到我们正题,经常使用mybatis的小伙 ...
- Google 官方 侧滑 drawerlayout
一.概述 目前侧滑框架已经很多了,但是我常用的也就那么2个 ,slidingmenu 和sidemenu-android, 但是项目要求使用官方的,所以就看了一下drawerlayout 二.代码 官 ...
- Android数据列表展示之 RecylerView
一.概述 1.RecyclerView是什么? RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式.该控件用于在有限的窗口中展示大量数据集,它被作为ListVie ...
- LeetCode第七题
Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 Have you ...
- [VB.NET Tips]字符串连接
在很多应用场景下我们都需要对字符串进行拼接操作. 在每一次连接字符串时,都要在堆上分配新的内存空间,每一个分配都有一定的消耗. 较长的字符串在堆中分配,对其进行连接操作需要花费很长的时间,先连接小的字 ...
- python + selenium 环境搭建及问题
搭建平台windows 准备工具如下: ------------------------------------------------------------- 下载python https://w ...
- 10.Django基础八之cookie和session
一 会话跟踪 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多次请求和响应.例如你给10086打个电话,你就是客户端,而10086服务人员就是服务器 ...
- 解决行内块元素(inline-block)之间的空格或空白问题
一.问题产生 由于html代码格式化后,标签会缩进或者换行.由于浏览器默认处理导致元素在页面显示中出现单个空格问题,尤其在行内或者行内块元素布局时影响比较明显 例如: 代码 页面显示 二.解决方案 这 ...