[深入理解JVM虚拟机]第2章-Java内存区域与内存溢出异常
2.0引-Java内存区域中,栈内存和堆内存分别装什么,为什么?
- 栈:解决程序的运行问题,即程序如何执行,或者说如何处理数据。
- 堆:解决的是数据存储的问题,即数据怎么放,放在哪儿。
参考链接https://www.cnblogs.com/gdufs/p/6407432.html
2.2运行时数据区
线程隔离的运行时数据区:程序计数器、Java虚拟机栈、本地方法栈
- 程序计数器:当前线程所执行的字节码的行号指示器,由于线程私有,故借助程序计数器线程切换后能恢复到正确的执行位置。
- Java虚拟机栈:每个方法在执行时会创建一个栈帧。每一个方法从调用至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
- 本地方法栈 ?todo
线程共享的运行时数据区:Java堆、方法区(其中包含运行时常量池)
- Java堆:存放对象实例。是垃圾收集器管理的主要区域。
- 方法区:存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。?
- 运行时常量池:是方法区的一部分。?todo
此外,直接内存不属于运行时数据区,但它也可能会产生内存溢出。Java中NIO类可以直接分配堆外内存。
2.3(基于HotSpot虚拟机)对象
对象的创建步骤
- step1:类加载。new指令的符号参数在常量池定位到类的符号引用,检查这个符号引用代表的类是否被加载、解析、初始化过,没有的话首先进行类加载。
- step2:为新对象分配内存。
- 问题一:如何划分可用空间:
- 方法一:指针碰撞。如果虚拟机堆中的内存是规整的,则把堆空闲内存指针移对象大小等大小距离即可。
- 方法二:空闲列表。如果堆内存不规整,就要维护一个空闲列表记录空闲的内存块。
- 问题二:修改指针所指向的位置,如何在并发情况下保证此操作是线程安全的。
- 方法一:对分配内存空间的动作进行同步:虚拟机采用CAS?+失败重试的方式保证更新操作的原子性。
- 方法二:本地线程分配缓冲(TLAB):即每个线程预先在队中分配一小块内存,是内存分配的动作在不同空间进行。只需在分配新TLAB时,进行同步锁定。选择是要用TLAB可通过参数-XX:+/-UseTLAB设定。
- 问题一:如何划分可用空间:
- step3:将分配到的内存空间初始化为零值。
- step4:对对象进行必要的设置。包括这个对象是哪个类的实例等。
- step5:从虚拟机角度,step1-4完成了对象的创建。但从Java程序视角来看,还需要对对象进行初始化(init方法)。
对象的内存布局
HotSpot虚拟机中,对象内存存储=对象头(Mark Word)+实例数据+对齐填充。
其中,对象头包含哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。它是一个非固定的数据结构,根据对象的状态复用自己的存储空间。
对象的访问定位
- 两种,一种是通过句柄访问对象,一种是通过直接指针访问对象。
- 通过句柄访问对象:Java堆中划分出一块内存作为句柄池,reference中存储的是对象的句柄地址,句柄中包含了对象实例数据地址(位于Java堆)及对象类型数据地址(位于方法区)
- 通过直接指针访问对象:reference中存储的就是对象地址,对象中包括:对象类型数据指针(位于Java堆)+对象实例数据(位于方法区)。
- 对比
- 不同点:就是reference存储的是什么、Java堆中是否有句柄池、对相关类型数据的地址存在句柄池还是对象的对象头中。
- 各自优势:句柄方式:reference中存储的是稳定的句柄地址,当对象被移动reference本身不需要修改;直接指针方式:快,节省了一次指针定位的开销。 - HotSpot使用直接指针访问方式。
2.4OutOfMemoryError异常
虚拟机内存除程序计数器外的其他几个运行时区域都可能发生OutOfMemoryError(OOM)异常。
2.4.1Java堆溢出
- 产生堆溢出:当把堆设置为不自动扩展(设置堆最小值-Xms参数=堆最大值参数-Xmx参数)时,不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么创建的所有对象超过堆的容量限制后,就会产生OutOfMemoryError异常。
- 排查方法:
- 步骤1 通过内存映象分析工具对Dump出来的堆转储快照?进行分析,重点是确认是内存泄漏(指内存中有许多不必要对象)还是内存溢出(内存中的对象都是有必要的)。
- 步骤2 如果是内存泄漏:通过工具,查看泄漏对象到GC Roots的引用链,就可以较准确地定位泄露代码的位置。
- 如果是内存溢出:在物理内存允许的情况下,调大虚拟机的堆参数;此外,检查代码中睡否某些对象生命周期过长,持有状态时间过长?。
2.4.2虚拟机栈和本地方法栈溢出
虚拟机参数:HosSpot虚拟机不区分虚拟机栈和本地方法栈,所以-Xoss参数(设置本地方法栈大小)是无效的。栈容量只由-Xss参数控制。
关于虚拟机栈和本地方法栈,Java虚拟机规范描述了两种异常:已使用的栈空间太大:StackOverflowError;内存太小:OutOfMemoryError。但是这本质是对同一件事情的两种描述而已。
由栈内存原因产生StackOverflowError异常:
- 单线程下:方法一:减少栈内存容量。方法二:定义大量本地变量。单线程下,经测试这两种方法均抛出StackOverflowError异常。
产生OutOfMemoryError异常:
- 多线程下:通过不断建立线程产生OutOfMemoryError(内存溢出)异常,但这与栈内存容量是否足够大并无关联。事实上,反而每个线程分配的栈内存越大,越容易造成OutOfMemoryError异常。因为OS给每个进程分配的内存(瓜分给:Xmx最大堆容量+MaxPermSize最大方法区容量+最大栈容量(包括虚拟机栈和本地方法栈)+程序计数器占用的(较小,可忽略不计)+虚拟机进程本身耗费的(暂略))是有限制的,所以若每个线程分配的栈内存越大,可以建立的线程数就越少,就越容易在建立线程时把内存耗尽。
排查方法:
- 针对StackOverflowError,可以阅读错误堆栈,找到问题所在。
- 多线程下,针对OutOfMemoryError,在不能减少线程数或更换64位虚拟机的情况下,则可以通过减少最大堆容量和减少栈容量来换取更多线程。
2.4.3方法区和运行时常量池溢出
- 方法区:
- 造成内存溢出:生成大量类。主流框架当有大量类增强,就可能会造成内存溢出。方法区溢出是常见的内存溢出异常,因为一个类被判定回收的条件非常苛刻。
- 需要注意方法区溢出的场景:动态生成大量类;使用CGLib字节码增强;动态语言;大量JSP的应用;基于OSGi的应用。
- 方法区中的运行时常量池:
- 造成内存溢出:JDK1.6中,String.intern()不断建立常量,且保持引用保证不被GC,会造成内存溢出。JDK1.7以后String.intern()使用引用的方法,不再复制,不会造成内存溢出。
- 排查方法:JDK1.6中,异常会提示OutOfMemoryError:PermGen space,说明是(HotSpot虚拟机)永久代的异常。JDK1.7后开始逐步“去永久代”。
2.4.4 本机直接内存溢出
- 参数: -XX:MaxDirectMemorySize 指定,否则默认与Java堆最大值一样。
- 排查方法:Heap Dump文件? 不会看到明显异常,如果发现OutOfMemoryError后Dump文件很小,程序中又使用了NIO,可以考虑是否是本机直接内存溢出。
[深入理解JVM虚拟机]第2章-Java内存区域与内存溢出异常的更多相关文章
- [深入理解JVM虚拟机]第3章-垃圾收集器、内存分配策略
垃圾收集器 判断对象是否需存活 回收堆 判断对象是否存活: 方法一:引用计数法.对象被引用一次就+1,当为0时回收对象.缺点:无法解决循环引用问题. 方法二:可达性分析算法.记录当前对象是否有和GC ...
- 深入理解JVM虚拟机11:Java内存异常原理与实践
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...
- Java工程师学习指南第6部分:深入理解JVM虚拟机
本文整理了微信公众号[Java技术江湖]发表和转载过的JVM虚拟机相关优质文章,想看到更多Java技术文章,就赶紧关注本公众号吧吧. JVM原理分析,看了都说好 JVM 深入学习:Java 解析 Cl ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 深入理解java虚拟机-第二章:java内存区域与内存泄露异常
2.1概述: java将内存的管理(主要是回收工作),交由jvm管理,确实很省事,但是一点jvm因内存出现问题,排查起来将会很困难,为了能够成为独当一面的大牛呢,自然要了解vm是怎么去使用内存的. 2 ...
- 深入理解java虚拟机系列(一):java内存区域与内存溢出异常
文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...
- 《深入理解JVM虚拟机》读书笔记
前言:<深入理解JVM虚拟机>是JAVA的经典著作之一,因为内容更偏向底层,所以之前一直没有好好的阅读过.最近因为刚好有空,又有了新目标.所以打算和<构架师的12项修炼>一起看 ...
- 深入理解JVM虚拟机1:JVM内存的结构与消失的永久代
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...
- 虚拟机--第二章java内存区域与内存溢出异常--(抄书)
这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第二章java内存区域与内存溢出 ...
随机推荐
- 调试备忘录-RS485 MODBUS RTU协议简述
目录--点击可快速直达 目录 写在前面 先简单说下什么是MODBUS? 参考文章 写在前面 最近在做和物联网有关的小项目,有一个传感器通讯用到了RS485 MODBUS RTU协议,所以就写个随笔记录 ...
- JavaScript在HTML中的基础用法总结
网页主要由三部分组成,分别为html.CSS和Javascript.如果说HTML是肉身,CSS是皮相,那Javascript就是灵魂.因此,三者的联系与融合则至关重要.本文就来为大家讲解一下Java ...
- 浏览器自动化的一些体会9 webBrowser控件之零碎问题3
WebBrowser控件最大的优点是可以轻松嵌入win form程序中,但是微软好像对这个控件没什么兴趣,这么多年了还没有改进,结果造成一堆问题. 1. 不支持https 2. 缺省模拟ie 7,如果 ...
- Typescript node starter 1.Express Typescript
启动项目 Express 是一个nodejs框架,用于构建Web后端应用程序.它非常的灵活,你可以用你喜欢的方式去使用他.在这个系列文章里,记录了我使用typescript express去构建一个w ...
- Java 8新特性(一):Lambda表达式
2014年3月发布的Java 8,有可能是Java版本更新中变化最大的一次.新的Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时 ...
- 第4篇 Scrum 冲刺博客(专✌️团队)
一.站立式会议 1.1会议图片 1.2成员完成情况 成员 昨天完成的任务 今天计划完成的任务 工作中的困难 陈忠明 按下载热度返回歌曲信息,与前端尝试交互 歌曲信息的上传/下载包 前后端交互问题 吴茂 ...
- Linux两台服务器mysql数据库同步
我们在做web系统部署的时候往往涉及到两台甚至多台数据库的备份,为了数据安全考虑(虽然说到底不过是一堆0 1,但是价值千金啊),所以我们还是乖乖做同步把! 1.准备两台Linux服务器(主.从) 2. ...
- Redis操作及集群搭建以及高可用配置
NoSQL - Redis 缓存技术 Redis功能介绍 数据类型丰富 支持持久化 多种内存分配及回收策略 支持弱事务 支持高可用 支持分布式分片集群 企业缓存产品介绍 Memcached: 优点:高 ...
- 数据去重Distinct,IEqualityComparer,IEquatable
很多情况下我们查询数据需要去重重复数据,下面就记录三个去重的方法. Distinct 最基本的去重形式,直接查询出数据后使用Distinct方法进行字段去重. var strList = new Li ...
- ARM开发板实现双系统引导的一种方法——基于迅为iTOP-4412开发板
前言 本文所用的uboot代码为迅为官方提供,开发板是迅为iTOP-4412开发板.本文如有错误,欢迎指正. 首先,我们确定一下系统启动的流程:首先启动uboot,uboot启动内核并挂载rootfs ...