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内存区域与内存溢出异常的更多相关文章

  1. [深入理解JVM虚拟机]第3章-垃圾收集器、内存分配策略

    垃圾收集器 判断对象是否需存活 回收堆 判断对象是否存活: 方法一:引用计数法.对象被引用一次就+1,当为0时回收对象.缺点:无法解决循环引用问题. 方法二:可达性分析算法.记录当前对象是否有和GC ...

  2. 深入理解JVM虚拟机11:Java内存异常原理与实践

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

  3. Java工程师学习指南第6部分:深入理解JVM虚拟机

    本文整理了微信公众号[Java技术江湖]发表和转载过的JVM虚拟机相关优质文章,想看到更多Java技术文章,就赶紧关注本公众号吧吧. JVM原理分析,看了都说好 JVM 深入学习:Java 解析 Cl ...

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

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

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

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

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

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

  7. 《深入理解JVM虚拟机》读书笔记

    前言:<深入理解JVM虚拟机>是JAVA的经典著作之一,因为内容更偏向底层,所以之前一直没有好好的阅读过.最近因为刚好有空,又有了新目标.所以打算和<构架师的12项修炼>一起看 ...

  8. 深入理解JVM虚拟机1:JVM内存的结构与消失的永久代

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

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

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

随机推荐

  1. mysql的ATM存取款机系统

    ##建库 CREATE DATABASE bankDB; ##客户信息表 CREATE TABLE userInfo ( customerID INT PRIMARY KEY AUTO_INCREME ...

  2. python设计模式之享元模式

    python设计模式之享元模式 由于对象创建的开销,面向对象的系统可能会面临性能问题.性能问题通常在资源受限的嵌入式系统中出现,比如智能手机和平板电脑.大型复杂系统中也可能会出现同样的问题,因为要在其 ...

  3. 题解 BZOJ4709

    题目描述 一道简单DP优化调了好久qwq 首先分析题目,发现每次从一边取贝壳是完全没用的,此题本质就是将区间分成数个区间,使区间价值和最大. 可以发现一个性质,那就是最优解的每个区间的两端点一定相同且 ...

  4. 【算法•日更•第三十一期】KMP算法

    ▎前言 这次要讲的HMP算法KMP算法很简单,是用于处理字符串的,之前一直以为很难,其实也不过如此(说白了就是优化一下暴力). ▎处理的问题 通常处理的问题是这样的:给定两个字符串s1和s2,其中s1 ...

  5. FCIS:Fully Convolutional Instance-aware Semantic Segmentation

    论文:Fully Convolutional Instance-aware Semantic Segmentation   目录 0.简介 1.Position-sensitive Score Map ...

  6. Jmeter 常用函数(17)- 详解 __substring

    如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.htm 作用 从字符串中获取指定范围的子串 语法格式 ...

  7. idea git拉取、合并、处理冲突、提交代码具体操作

    早在两个月前我还在用eclipse开发,并且也发布的一些eclipse git的相关操作(操作都是本人亲自实践过的),但由于项目团队要求,开发工具统一用idea,实在不得已而为之切换了开发工具, 初次 ...

  8. 第二篇Scrum冲刺博客--Interesting-Corps

    第二篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 主页页面和探测空间设计及布局实现 主页页面跳转社区功能及社区设计及布局实现 叶学涛 设计页面 ...

  9. Java多线程_同步工具CountDownLatch

    概念:CountDownLatch是多线程里面一个类似于计数器的高级同步工具,它的初始值代表线程的数量,当一个线程完成了任务后,CountDownLatch的值就减1,当值为0的时候,代表所有线程完成 ...

  10. 手写区分PC还是手机移动端

    区分首先要了解window.navigator 输出navigator appCodeName: "Mozilla" appName: "Netscape" a ...