深入理解Java虚拟机:Jvm总结-Java内存区域与内存溢出异常

第二章 Java内存区域与内存溢出异常

2.1 意义

对于C、C++程序开发来说,程序员需要维护每一个对象从开始到终结。Java的虚拟自动内存管理机制,让java程序员不需要手写delete或者free代码,不容易出现内存泄漏和内存溢出问题,但是如果出现了内存泄漏和溢出的问题,就需要知道虚拟机是怎样使用内存的,才能排查错误并且修正。本章主要讲明Java虚拟机内存的各个区域,各个区域的作用、服务对象,以及常见的异常。

2.2 运行时数据区域

Java虚拟机在执行Java程序的过程中会把管理的内存分为几个不同的数据区域,其中包括以下几个运行时数据区,这些区域有各自的用途以及创建和销毁的时间。
在这里插入图片描述

2.2.1 程序计数器

  • 程序计数器占用了一块较小的内存空间,充当线程指示器的角色。

  • Java虚拟机的多线程是通过线程之间切换、分配处理器执行时间实现的,一个内核在某一时刻只会执行一条线程的指令,所以计数器就可以在切换线程时发挥作用,能够确定切换到正确位置继续程序执行。而且要保证每个线程的计数器是独立的,互不干涉。

  • 如果执行的Java方法,计数器记录的是正在执行的虚拟机字节码的地址。如果是native方法,计数器为空(undefined)。

  • 唯一一个没有OutOfMemoryError情况的区域

2.2.2 Java虚拟机栈

  • 线程私有的,生命周期与线程相同。

  • 虚拟机栈对应Java方法执行,每个方法对应栈中的一个栈帧(每个方法被调用到执行完毕的过程就对应一个栈帧在虚拟机栈中入栈到出栈),这个栈帧存储局部变量表、操作数栈、动态连接、方法出口等信息。

  • 局部变量表存放编译期可知的基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress类型。

    局部变量表的的存储空间以局部变量槽(slot)表示,long和double占两个slot,其他类型一个slot。

    栈帧在编译期就给局部变量表分配好了内存空间,即slot的数量在方法运行期间不会改变。

  • 两类异常:线程请求的栈深度大于虚拟机的所允许的深度,抛出StackOverflowError异常;

    Java虚拟机栈容量可以动态扩展的情况下,扩展时无法申请到足够的内存会抛出OutOfMemoryError异常

2.2.3 本地方法栈

与虚拟机栈执行Java方法相似,只不过本地方法栈执行Native方法。没有强制规定,有的Java虚拟机(Hot-Spot)直接合二为一。

2.2.4 Java堆

  • 所有线程共享,虚拟机启动时创建
  • 唯一目的是存放对象实例,几乎所有的对象实例都在堆上分配内存
  • 是垃圾收集器管理的内存区域,基于分代收集理论逻辑上分为“新生代”“老年代”“永久代”“Eden空间”“From Survivor空间”“To Survivor空间”等。
  • 可以处于物理不连续的内存空间,但逻辑上是连续的。
  • 参数-Xmx(设置 Java 堆内存的最大大小)和-Xms(设置初始堆内存大小)可以控制是否可扩展
  • 如果堆中没有内存完成实例分配,且堆无法扩展,会抛出OutOfMemoryError异常。

2.2.5 方法区

  • 各个线程共享,别名“非堆”
  • 用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据
  • 可以不实现垃圾回收,主要因为回收效果不太好,回收条件苛刻,但有时有必要
  • 无法满足新的内存分配会抛出OutOfMemoryError异常
  • *方法区的实现可以由JVM实现自由决定,JDK8以前称为永久代,但不等价,为了让垃圾收集器方便管理这部分内存。JDK8以后改为元空间

2.2.6 运行时常量池

  • 方法区的一部分,存放编译期生成的字面量(比如字符串或基本类型的常量)和符号引用(类和接口的全限定名:例如,java.lang.String。字段的名称和描述符:例如,int age中的字段名称age和类型描述符int。方法的名称和描述符:例如,void print(String s)中的方法名称print和描述符(Ljava/lang/String;)V
  • 一般会把符号引用翻译的直接引用也存储在运行时常量池中
  • 具备动态性,运行期间也可以放入新的常量,String类的intern()方法
  • 无法申请到内存会抛出OutOfMemoryError异常

2.2.7 直接内存

  • 不是运行时数据区的一部分
  • 使用Native函数分配堆外内存,再通过堆内的一个对象(DirectByteBuffer)引用这块内存来使用,提高性能
  • 也会出现OutOfMemoryError异常

2.3 Hotspot虚拟机对象探秘

HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程

2.3.1 对象的创建过程

  1. new一个对象时,Java虚拟机会首先检查这个指令的参数能否在常量池中定位到一个类的符号引用,然后判断是否已经被加载、解析和初始化过。如果没有则先执行类加载过程,然后为新生对象分配内存。
  • 如果内存规整,一边是使用过的内存,一边是空闲的内存,由指针分隔,那么分配内存就只需要移动指针来完成,这就是指针碰撞。

  • 实际上内存并不规整,所以虚拟机通过一个列表来记录哪些内存块是可用的,然后从列表中找到一块空间分配给对象实例,这就是空闲列表。

  • 堆是否规整由垃圾收集器有没有整理能力决定。

为了并发情况下保证创建对象的线程安全,有以下两种方案:一般情况本地缓冲区用完再进行同步处理。

  • 对分配内存的行为进行同步处理:实际中虚拟机采用CAS(Compare-And-Swap比较当前值和预期值,如果它们相等,则将当前值更新为新值。否则,什么也不做。)+失败重试(当多个线程同时尝试更新同一个变量时,只有一个线程的CAS操作会成功,其他线程会失败。失败的线程不会阻塞,而是重新读取变量的当前值并再次尝试操作)
  • 每个线程在堆中预先分配一块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
  1. 分配完后,将分配到的内存初始化为零值,不包括对象头。
  2. 然后根据对象头里的信息进行设置。
  3. 接着执行构造方法()方法, 一个完整的对象就被创建出来了。

2.3.2 对象的内存布局

一个对象在堆中的布局为三个部分:对象头(header)、实例数据(Instance data)、对齐填充(Padding)

  1. 对象头包括两类信息
    • Mark Word:对象自身的运行时数据,动态数据结构
    • 类型指针:指向类型元数据,确定该对象是哪个类的实例。(使用句柄访问则不需要保存类型指针,类型指针都存到了句柄池中)
    • 如果是数组还需要记录数组长度
  2. 对象真正存储的有效信息,各种类型的字段内容。
    • 存储顺序由虚拟机和java源码决定
    • 父类定义的变量会在子类之前
  3. 对齐填充起占位符的作用,保证8字节的整数倍

2.3.3 对象的访问定位

由虚拟机实现决定,主流有两个:使用句柄或者直接指针

  • 使用句柄,reference存储对象的句柄地址,句柄指向实例数据和类型数据。对象被移动时只会改变句柄中的指针,而reference不需要修改,如图

在这里插入图片描述

  • 直接指针,对象自身保存指向类型数据的指针,不需要多一次间接指针的开销,hotspot使用。如图

在这里插入图片描述

2.4 OutOfMemoryError异常

验证运行时区域存储的内容,以及遇到内存溢出异常时,通过信息得知是哪个区域的内存溢出,知道怎样的代码可能会导致这些区域内存溢出,以及出现这些异常后该如何处理。

2.4.1 Java堆溢出

参数-XX:+HeapDumpOnOutOf-MemoryError可以让虚拟机在出现内存溢出异常的时候Dump出当前的内存堆转储快照以便进行事后分析

Java堆内存的OutOfMemoryError异常,会跟随进一步提示“Java heap space“ ,处理方法:

  • 通过内存映像分析工具对dump出来的堆转储快照进行分析,确认导致oom的对象是否必须(内存泄露还是内存溢出)

  • 如果内存泄露:

    查看泄露对象到GC roots的引用链,定位对象创建的位置,找到出现内存泄漏的代码的具体位置

  • 如果内存溢出:

    • 检查堆参数设置,查看是否还有调整空间
    • 检查是否某些对象生命周期过长、持有状态过长或存储结构设计不合理

2.4.2 虚拟机栈和本地方法栈溢出

Java虚拟机规范中描述了两种异常:

  1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  2. 如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出
    OutOfMemoryError异常。

对于HotSpot虚拟机,不区分虚拟机栈和本地方法栈,所以栈容量只由-Xss设定,而且不支持栈的动态扩展。所以除非创建线程时就内存不足才会出现OutOfMemoryError异常。只会在运行时栈无法容纳新的栈帧抛出StackOverflowError异常。

如果出现了建立过多的线程导致的内存溢出,可以考虑减少最大堆和减少栈容量来换取更多的线程。因为windows给一个进程分配的内存是有限的。

2.4.3 方法区和运行时常量池溢出

首先明白在HotSpot中方法区表现形式的变化,如下图

在这里插入图片描述

*方法区的实现可以由JVM实现自由决定,尽管JVM规范将方法区描述为非堆内存区域,但运行时常量池在Java 8之后确实被放置在堆内存中。这意味着,运行时常量池虽然是方法区逻辑上的一部分,但在实际的JVM实现中,它的存储位置可以是堆内存。字符串常量池同理

为什么运行时常量池在堆内存中

  • 动态性:运行时常量池中的常量不仅包括类文件中定义的常量,还可以在运行时动态添加。这种动态性需要灵活的内存管理,堆内存提供了这样的灵活性。
  • 垃圾回收:运行时常量池中的常量可能在程序运行过程中变得不再需要。将其放在堆内存中,JVM可以利用堆内存的垃圾回收机制更高效地回收这些不再使用的常量。

回到溢出问题,String::intern()是一个本地方法,作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用,否则会将此String对象包含的字符串添加到字符串常量池中,并返回引用。

jdk7之前,运行时常量池属于方法区,且大小固定容易溢出。之后转移到堆上以后不容易溢出。

2.4.4 本机直接内存溢出

直接内存(Direct Memory)的容量大小可通过-XX:MaxDirectMemorySize参数来指定,如果不去指定,则默认与Java堆最大值(由-Xmx指定)一致

直接内存导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见有什么明显的异常情况,如果内存溢出之后产生的Dump文件很小,而程序中又直接或间接使用了DirectMemory(典型的间接使用就是NIO),有可能是直接内存的问题。

  • 从GC Roots找引用链
  • Stop The World,因为如果对象引用关系还在不停变化,会影响收集的准确性
  • 虚拟机应当可以直接得到哪些地方记录了对象引用,比如OopMap,会记录栈和寄存器里引用的位置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/53786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络安全】-文件上传漏洞

文件操作漏洞包括文件上传漏洞,文件包含漏洞,文件下载漏洞。 文章目录 前言 什么是文件上传漏洞? 文件上传的验证与绕过: 1.前端js验证:   Microsft Edge浏览器: Google Chrome浏览器: 2.后端…

Taro实现微信小程序自定义拍照截图识别

效果图&#xff1a; 代码&#xff1a; <template><view class"lary-top" :style"{ height: ${topBarHight}px }"></view><Camerav-show"!canvasShow"class"camera-photo":style"{width: ${info.windowWidt…

LIO-SAM如何保存地图

一、找到LIO-SAM配置文件&#xff0c;路径为config/params.yaml&#xff0c;修改以下两项参数&#xff1a; savePCD: true # https://github.com/TixiaoShan/LIO-SAM/issues/3savePCDDirectory: "/home/slam/catkin_ws/src/maps" …

Http带消息头两种请求办法

API接口最近经常碰到&#xff0c;协调几个乙方来回对接&#xff0c;把我折腾晕了&#xff0c;索性自己写一个小的工具&#xff0c;导入历史数据。 获取平台免登录token 接口说明 URL Path&#xff1a;gateweb/bigm-dm/openApi/ologin/openLogin 说明&#xff1a;第三方免登…

Java架构师实战篇Redis亿级数据统计方案

目录 1 Redis亿个keys数据统计方案2 Redis聚合统计(SUNIONSTORE)3 Redis排序统计(LRANGE)4 值状态统计(bitmap)4.1 位图简介4.2 应用场景4.3 常用的命令4 基数统计(SADD)5 总结想学习架构师构建流程请跳转:Java架构师系统架构设计 1 Redis亿个keys数据统计方案 在 Web 和移动…

.NET 一款用于解密web.config配置的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

SpringBoot2:请求处理原理分析-接口参数解析原理(argumentResolvers)

一、知识回顾 我们知道&#xff0c;接口的参数&#xff0c;一般都要配上注解来一起使用。 不同的参数注解&#xff0c;决定了传参的方式不同。 为什么会这样&#xff1f; 如果让你设计接口参数解析&#xff0c;你会怎么做&#xff1f; 首先&#xff0c;我们知道方法参数是形…

机器学习之监督学习(三)神经网络

机器学习之监督学习&#xff08;三&#xff09;神经网络基础 0. 文章传送1. 深度学习 Deep Learning深度学习的关键特点深度学习VS传统机器学习 2. 生物神经网络 Biological Neural Network3. 神经网络模型基本结构模块一&#xff1a;TensorFlow搭建神经网络 4. 反向传播梯度下…

开源的 Windows 12 网页体验版!精美的 UI 设计、丰富流畅的动画

大家周二好呀&#xff01;博主今天给小伙伴们分享一款炫酷的 Windows 12 体验版&#xff0c;网页效果拉满&#xff0c;非常值得我们去尝试&#xff01; 如果你对未来的Windows操作系统充满期待&#xff0c;那么这款开源的Windows 12 网页体验版绝对不容错过&#xff01;这不仅…

consul配置ACL安全认证

文章目录 前言一、漏洞详情二、漏洞处理1.ACL相关介绍2.开启ACL3.创建令牌4.修改acl文件5.修改单节点consul启动配置文件6.重启consul 三、漏洞处理结果验证 前言 因为现阶段属于护网期,因此公司对服务器、业务的安全都很关注,只要再次期间被漏扫出来的漏洞&#xff0c;都需要…

tomcat是不是已经被Springboot集成了?

Tomcat 确实已经被 Spring Boot 集成。Spring Boot 通过其独特的“starter”模块&#xff08;如spring-boot-starter-web&#xff09;实现了对Tomcat的自动配置。当在Spring Boot项目中引入这个starter时&#xff0c;会自动引入相关的依赖&#xff0c;包括tomcat-embed-core、t…

C语言补习课——文件篇

来源&#xff1a;黑马程序员 第157讲 C语言操作文件概述 读取文件&#xff1a;输入流 写文件&#xff1a;输出流 读写的方向判断取决与参照&#xff0c;一般我们站在程序的角度判断读写方向。 第158讲 路径 基本概念 路径就是指文件在电脑中的位置&#xff0c;eg&#xf…

阿里旗下土耳其电商Trendyol计划进军欧洲市场

阿里旗下土耳其电商Trendyol计划进军欧洲市场 近年来&#xff0c;阿里巴巴集团在全球电商领域的布局持续深化&#xff0c;其旗下土耳其电商巨头Trendyol更是凭借其出色的市场表现和强劲的增长势头&#xff0c;成为了备受瞩目的焦点。近日&#xff0c;Trendyol宣布了一项重要战…

Java初步

背景 名字 Oak&#xff08;橡树&#xff09;改到一个岛&#xff08;疑似盛产咖啡&#xff09; Java之父 詹姆斯高斯林&#xff08;James Gosling&#xff09; sun公司研发 现在属于Oracle公司 把它称为甲骨文公司 应用 企业级应用开发 重点学习JavaSE&#xff08;Standa…

线性代数 第六讲 特征值和特征向量_相似对角化_实对称矩阵_重点题型总结详细解析

文章目录 1.特征值和特征向量1.1 特征值和特征向量的定义1.2 特征值和特征向量的求法1.3 特征值特征向量的主要结论 2.相似2.1 相似的定义2.2 相似的性质2.3 相似的结论 3.相似对角化4.实对称矩阵4.1 实对称矩阵的基本性质4.2 施密特正交化 5.重难点题型总结5.1 判断矩阵能否相…

JavaScript高级——函数中的this

1、this是什么&#xff1f; ① 任何函数本质上都是通过某个对象来调用的&#xff0c;如果没有直接指定就是 window 。 ② 所有函数内部都有一个变量 this 。 ③ 它的值是调用函数的当前对象。 2、如何确定 this 的值&#xff1f; ① test&#xff08;&#xff09;&#xff…

如何进行DAP-seq的数据挖掘,筛选验证位点

从样本准备到寄送公司&#xff0c;每一天都在“祈祷”有个心仪的分析结果&#xff0c;终于在这天随着邮件提示音的响起&#xff0c;收到了分析结果...... 分析前工作 爱基在进行数据分析之前&#xff0c;会有两次质控报告反馈给老师们。第一个&#xff0c;基因组DNA的提取质控…

springBoot 集成https

springBoot 集成https 1、springBoot默认的证书格式 pring Boot 需要 .p12 或 .jks 格式的证书。如果你只有 .pem 和 .key 文件&#xff0c;可以使用 openssl 工具将它们转换成 .p12 文件 2、转换.p12 我的证书文件如下&#xff0c;需要转换 2.1 下载openssl https://slpr…

苹果手机铃声怎么设置自己的歌?3个方法自定义手机铃声

苹果手机内部的手机铃声库只有固定的几首铃声&#xff0c;且都是纯音乐&#xff0c;比较单调&#xff0c;并不是所有用户都喜欢这些铃声。那么&#xff0c;苹果手机铃声怎么设置自己的歌呢&#xff1f;小编这里有3个方法&#xff0c;可以教大家如何将手机铃声设置成自己喜欢的歌…

Java 入门指南:Java 并发编程 —— 同步工具类 Semephore(信号量)

文章目录 同步工具类Semephore核心功能限制并发访问量公平与非公平策略灵活性与适应性 常用方法使用示例 同步工具类 JUC&#xff08;Java.util.concurrent&#xff09;是 Java 提供的用于并发编程的工具类库&#xff0c;其中包含了一些通信工具类&#xff0c;用于在多个线程之…