HotSpot虚拟机对象探索与OutOfMemoryError异常

news2024/10/7 6:38:44

HotSpot虚拟机对象探索与OutOfMemoryError异常

1.HotSpot虚拟机对象探索

1.1对象的创建

不是一直有一个笑话,别人问程序员有没有对象,程序员会说我没有对象,但是我可以new一个出来

这里就可以判断他学过c++或者java等语言

在java中对象的创建一般我们都是通过new来创建的,但是这针对的只是java的语言层面,如果在虚拟机的层面上来说的话,东西还很多

1.1.1虚拟机方面

我们来看看下面的代码

String a = "123";
String b = new String("123");

请问这里面创建了多少个对象

答案是两个,分别是第一行的"123"与第二行的new String()

new String我们都知道,只要用了这个方法那么在堆内存。但是为什么只有上面的123创建了对象,下面new的那个123没有创建对象呢?

这是因为java虚拟机在创建对象前会先去检查在常量池里面能否定位到一个类的符号引用,因为我们在第一行在常量池里面已经创建了一个123对象,在第二行中123对象可以在常量池里面找到,所以总共就创建了两个对象

如果我们最开始没有

String a = "123";

那么在执行

String b = new String("123");

的时候我们会在常量池里面创建123,然后将在堆内存中创建一个新的String对象,其值为123,然后就要为这个对象从堆内存分配内存,

1.1.1.1堆内存的分配方式

堆内存给对象分配空间是有2种情况的

  1. 使用过的内存放在一边,空闲的内存放在另一边
  2. 使用过的内存和空闲的内存交织在一起

为什么会有这两种情况的产生主要是因为所采用的垃圾收集器是否带有空间压缩整理能力决定的

如果带有的话,则会采用指针碰撞这种方式来进行第一种分配内存的方式

如果没有的话,则会采用空闲列表这种方式来进行第二种分配内存的方式

1.1.1.2并发情况下对象的创建

在并发情况下,可能出现正在给A对象分配内存,指针还没来的及修改,对象B又同时使用了原来的指针来分配内存,解决这个有两种方法

  1. 使用同步处理,让A执行完后才让B执行
  2. 使用本地线程分配缓冲,即每个线程在java堆中预先分配一小块内存,哪个线程要分配内存,就在哪个线程的本地缓冲区进行分配

我最开始以为这个本地线程分配缓冲工作内存是一个概念。后来发现不是

本地线程分配缓冲是对分配内存的,而工作内存是线程对一些变量或常量进行操作从某个地方取的

1.1.1.3对对象的设置

Java虚拟机还要对对象进行必要的设置,并把这些信息存放在在对象的对象头(Object Header)之中,会将对象的所有实例变量设置为其默认值。默认值取决于实例变量的类型。例如,对于基本类型(如int、float、boolean等),默认值为0或false。对于对象引用类型,默认值为null。

1.1.2Java程序角度

在虚拟机方面我们其实主要干了两件事,第一件事是分配对象的内存空间,第二件事是把一些基本信息传递给对象

虚拟机的弄完并不代表着对象就创建完了,我们需要在java程序上进行构造函数(记住,无参构造是默认的,一般你不写都会进行无参构造。有参构造得你自己写)

构造函数的主要作用就是把对象需要的状态和其他信息传递给对象

至此,java中的对象就创建好了

1.2对象的内存布局

对象的内存布局主要可以分为3个,分别是对象头,实例数据,对齐填充

1.2.1对象头

对象头主要是由两个构成的分别是存储对象自身运行时的数据类型指针

这个构成看起来很像c语言中的链表,c语言链表中也是由数据还有指针构成的

这个存储对象自身运行时的数据并不和下面那个实例数据起冲突

它主要存储的是自身运行时的数据,如

哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等等

在这里面我只知道锁状态标志和线程持有的锁,其他不是很能理解,悲

这些数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别位32个比特和64个比特

上面提到的哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳的对象头的数据被称为Mark Word

类型指针就是对象指向它的类型元数据的指针,Java虚拟机可以通过这个类型来确定该对象为哪个类的实例。

但是要记住一句话:并不是所有的虚拟机实现都必须在对象数据上保留类型指针

1.2.2实例数据

存储在代码中定义的各种类型的字段内容,无论是从父类继承还是在子类定义的字段都得记录下来

1.2.3对齐填充

单纯占位符

1.3对象的访问定位

Java中对象创建好了,我们需要去使用对象,一般都是通过虚拟栈中的reference对象去操作堆上的具体对象

这个reference对象就是我们之前说过的存放在Java虚拟机栈的对象

我们上面不是写过两端代码嘛

String a = "123";
String b = new String("123");

其中,a和b就是这个reference对象

在Java中主流的访问有使用句柄和直接指针两种

1.3.1使用句柄

使用句柄访问的话,java堆可能会划分出一块内存来作为句柄池,引用对象存储的就是对象的句柄地址,而句柄中包含了对象的实例数据与类型数据各自的地址信息

1.3.2直接指针

java堆中对象的内存布局就必须考虑如何放置访问类型数据信息,引用数据中存储的直接就是对象地址

1.3.3对这两种访问方式的理解

无论是使用句柄还是直接指针,我们的引用对象都得通过java堆来访问

但是区别就在于如果是直接指针的话,到对象类型数据的指针就在对象实例数据中,我们可以直接通过这个指针指向方法区对象类型数据

但是如果是使用句柄,它会在堆内存中专门再划分一块区域,用来存放两个指针,一个就是到对象实例数据的指针,一个是到对象类型数据的指针。两个指针分开指,到对象实例数据的指针指向堆内存对象实例数据到对象类型数据指向的就是方法区对象类型数据

这里面刚好就和上面的内容联系起来了,这个指向对象类型数据的指针就是我们刚才在对象头的那个指针

用图来表示以下就是这样

1.3.4两者的好处

使用句柄访问的话,引用数据存储的就是句柄地址,在对象被移动的时候只会改变句柄中的实例数据指针,引用数据本身不用改变

而使用直接指针访问的话,速度更快

1.4总结

对象的创建主要是对象内存的分配与对象的引用

对象内存的分配主要是两个方面,一个是虚拟机上我们给它分配内存,从堆内存分点空间给它分点空间,然后第二个是在java的无参构造或者有参构造把一些属性给它

对象的引用也是两个方法,第一个是句柄访问对象,这个方法是堆内存给它分一部分空间成为句柄空间,然后一个指向类型数据的指针指向方法区的类型数据,一个指向实例数据的指针指向堆内存的实例池的实例数据

第二个是直接访问对象,这个方法是类型数据指针就在实例数据里面,然后类型数据指针指向方法区的类型数据

2.OutofMemoryError异常

在Java中除了程序计数器以外,虚拟机的其他都容易出现OutofMemoryError异常

2.1Java堆溢出

Java的堆溢出主要是因为Java的堆内存是有大小限制的,因为我们的Java堆内存是用来存储Java对象实例的,所以要是我们Java的实例对象创建的太多的话,就容易引起Java堆溢出

2.1.1如何解决Java堆溢出

我们首先是确认内存中导致OOM的对象是否是必要的,也就是到底是内存泄漏还是内存溢出

如果是内存泄漏的话我们要找到是什么导致的GC回收器没办法回收它

如果是内存溢出那就代表着内存中的每一个对象都得存活下来,那么就得检查Java虚拟机的参数,看是否可以向上调整内存的空间。然后再看哪些对象生命周期过长,持有状态时间过长,存储结构设计不合理等情况,尽量减少程序运行期的内存消耗

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

HotSpot虚拟机不区分虚拟机栈和本地方法栈,所以本地方法栈在这里面没什么用,

在虚拟机栈中主要有以下两种异常

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

第一个就可以举一个这2个例子

public static void infiniteRecursion() {
    infiniteRecursion();
}

这个函数如果无限的自己调用自己,那么就会有大量的局部变量.方法参数,临时变量存放在虚拟机栈中,就有可能导致栈溢出

还有这个

public static void createLargeObjects() {
    for (int i = 0; i < 1000000; i++) {
        byte[] largeObject = new byte[1000000];
    }
}

每一次循环创建的东西比较多,然后又进行了很多次的循环,就容易导致栈溢出

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

在 JDK 7 及之前的版本中,运行时常量池是存放在 PermGen 中的,PermGen 是方法区的一部分,用于存放类的元数据信息、静态变量、即时编译器编译后的代码等。由于运行时常量池的大小是在编译期间就确定的,所以将其存放在 PermGen 中是比较合适的。

但是,随着 Java 应用程序的发展,动态生成和卸载类的需求越来越多,而 PermGen 的大小是固定的,无法动态调整。这就导致了 PermGen 空间容易被占满,从而触发 Full GC,影响了应用程序的性能和稳定性。因此,为了解决这个问题,从 JDK 8 开始,PermGen 被彻底移除,Java 堆中的元数据区(Metaspace)被用来代替 PermGen 存储类的元数据信息。

而运行时常量池也随之从 PermGen 中移动到了 Java 堆中的元数据区。这样做的好处是,运行时常量池的大小可以根据应用程序的需要进行动态调整,从而更好地满足应用程序的需求。同时,也避免了 PermGen 空间被占满的问题,提高了应用程序的性能和稳定性。

2.4本地直接内存溢出

直接内存的容量大小可通过**-XX:MaxDirectMemorySize**参数来指定,如果不指定就和Java堆内存的最大值一样

直接内存无法直接进行分配的时候就会造成直接内存溢出

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

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

相关文章

MyBatis01

ORM&#xff1a;对象关系映射 O&#xff08;Object&#xff09;&#xff1a;Java虚拟机中的Java对象 R&#xff08;Relational&#xff09;&#xff1a;关系型数据库 M&#xff08;Mapping&#xff09;&#xff1a;将Java虚拟机中的Java对象映射到数据库表中一行记录&#xff0…

【王道·操作系统】第三章 内存管理

一、内存管理 1.1 内存的基础知识 内存可存放数据&#xff0c;程序执行前需要先放到内存中才能被CPU处理——缓和CPU与硬盘之间的速度矛盾内存地址从0开始&#xff0c;每个地址对应一个存储单元 按字节编址&#xff1a;每个存储单元大小为1字节(B)&#xff0c;即8个二进制位按…

【Spring Cloud系列】- Eureka使用详解

【Spring Cloud系列】- Eureka使用详解 文章目录 【Spring Cloud系列】- Eureka使用详解一、概述二、Eureka简介三、Eureka结构与作用Eureka结构图Eureka采用CS&#xff08;Client/Server,客户端/服务器&#xff09;架构&#xff0c;它包括以下两大组件 四、Eureka集群及与应用…

梁宁:为什么中国没有像 ChatGPT 和 Vision Pro 这样的创新产品?

6 月 10 日&#xff0c;产品战略专家梁宁和图灵联合创始人刘江围绕“ ChatGPT 真需求”主题进行直播对谈。 梁宁&#xff0c;产品战略专家&#xff0c;曾任湖畔大学产品模块学术主任&#xff0c;联想、腾讯高管&#xff0c;CNET集团副总裁。 工作经历横跨 BAT&#xff0c;与美团…

第14届蓝桥杯Scratch(中级)国赛真题解析2023.5.28

第14届蓝桥杯Scratch(中级)国赛真题解析2023.5.28 一:选择题(50分)第 1 题 单选题(10分) 运行以下程序后,角色说出的数是 ( C )。 *选择题严禁使用程序验证,选择题不答或答错都不扣分 A.150 B.200 C.300 D.600第 2 题 单选题(10分) 对以下程序效果描述完全正确的是 …

【JUC进阶】01. Synchroized实现原理

目录 1、前言 2、Synchronized使用 2.1、对象锁&#xff08;Instance Lock&#xff09; 2.2、类锁&#xff08;Class Lock&#xff09; 2.3、方法锁&#xff08;Method Lock&#xff09; 3、原理分析 3.1、monitor对象 3.2、monitorenter 3.3、monitorexit 3.4、对象…

库克和马斯克之后,比尔盖茨访华,美企认识到中国市场不可替代

微软创始人比尔盖茨已到达了中国&#xff0c;这是美国苹果CEO库克、特斯拉CEO马斯克之后访华的又一位美国重要企业家&#xff0c;那么美国企业家陆续访华是为了什么呢&#xff1f; 一、美企连遭挫折 苹果一季度的业绩显示营收、利润均下滑了个位数&#xff0c;但是苹果看到了隐…

AIGC 加持 Cocos,游戏开发需要几步?

近日&#xff0c;游戏行业知名的 B2B 大会 WN 2023 大会于土耳其首都伊斯坦布尔顺利举办。本次大会邀请了来自全球的游戏开发商、媒体、发行商、分发平台等行业决策者&#xff0c;共同探讨游戏行业未来发展态势&#xff0c;进一步拓展业务&#xff0c;并在世界范围内寻找新的合…

【力扣刷题 | 第十天】347.前k个高频元素 227 简单计算器

前言&#xff1a; 本篇将是最后一篇我们利用栈与队列来解决力扣问题&#xff0c;在下文我们将进入到数这一章&#xff0c;相对应的【夜深人静讲数据结构与算法】专栏中树也会及时更新。 347. 前 K 个高频元素 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums 和…

【JS】1714- 重学 JavaScript API - Geolocation API

❝ 前期回顾&#xff1a; 1. Page Visibility API 2. Broadcast Channel API 3. Beacon API 4. Resize Observer API 5. Clipboard API 6. Fetch API 7. Performance API 8. WebStorage API 9. WebSockets API 10. Fullscreen API ❞ 本文将深入探讨 Geolocation API 的概念、使…

华为OD机试真题 JavaScript 实现【关联子串】【2023Q1 100分】,附详细解题思路

一、题目描述 给定两个字符串str1和str2&#xff0c;str1进行排列组合只要有一个为str2的子串则认为str1是str2的关联子串&#xff0c;请返回子串在str2的起始位置&#xff0c;若不是关联子串则返回-1。 二、输入描述 qwe dsgfasgfwe 三、输出描述 -1 四、解题思路 读取…

009、体系架构之HTAP

HTAP HTAP技术传统的HTAP解决方案HATP的要求TiDB的HTAP架构TiDB的HTAP特性使用场景 MPP HTAP技术 传统的HTAP解决方案 HATP的要求 可扩展性 分布式事务分布式存储 同时支持OLTP与OLAP 同时支持行存和列存OLTP与OLAP业务隔离 实时性 行存与列存数据实时同步 TiDB的HTAP架构 …

Committer 迎新!这次是来自阿里云的同学

点击蓝字 关注我们 迎新&#xff01; 截至今天&#xff0c;Apache DolphinScheduler 项目在 GitHub 上的 Star 数已突破 10.6K&#xff0c;贡献者人数也突破了 470 人。社区的不断壮大&#xff0c;离不开每位 Contributor 的支持。 最近&#xff0c;Apache DolphinScheduler 又…

AI模型部署实战:利用CV-CUDA加速视觉模型部署流程

本文首发于公众号【DeepDriving】&#xff0c;欢迎关注。 CV-CUDA简介 随着深度学习技术在计算机视觉领域的发展&#xff0c;越来越多的AI算法模型被用于目标检测、图像分割、图像生成等任务中&#xff0c;如何高效地在云端或者边缘设备上部署这些模型是工程师迫切需要解决的问…

Android 13(T) - 智能指针

Android有一套自己的智能指针管理办法&#xff0c;并且将其运用在源码的各个角落&#xff0c;所以学习Media框架之前&#xff0c;我们有必要先了解下Android智能指针。 本节代码源自于Android 13(T)&#xff0c;参考 (aospxref.com) 1 概述 与智能指针相关的总共有5个类&#…

某小厂面试加答案(6.15)

看 Java 面试题就去 www.javacn.site 磊哥新推出《企业面经和答案》栏目&#xff0c;最近会持续更新&#xff0c;欢迎大家订阅此账号查看&#xff0c;或访问 www.javacn.site 查看。 面经来源于牛客&#xff0c;如下图所示&#xff1a; https://www.nowcoder.com/feed/main/det…

OpenAI的创始人World Coin项目介绍

&#x1f3af; 在一个崇高的目标支持下&#xff0c;不停地工作&#xff0c;即使慢&#xff0c;也一定会获得成功。—— 爱因斯坦 如果你对项目感兴趣请联系v&#xff1a;weixin605405145 一、项目速览 项目背景 Worldcoin由OpenAI的创始人Sam Altman于2019年创立&#xff0c;就…

【C++】的继承

继承的概念及定义 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结构…

010、体系架构之TiFlash

TiFlash TiFlash 功能架构异步复制一致性读取场景选择是选择TiKV还是TiFLash TiFlash 功能 异步复制一致性读取(写虽然是异步&#xff0c;但读可以做到一致性)引擎智能选择计算加速 架构 TiFLASH 也是通过raft 算法进行同步&#xff0c;但它不怎么消耗资源&#xff0c;因为它…

ProGuard 进阶系列(二)配置解析

书接上文&#xff0c;从开源库中把代码下载到本地后&#xff0c;就可以在 IDE 中进行运行了。从 main 方法入手&#xff0c;可以看到 ProGuard 执行的第一步就是去解析参数。本文的内容主要分析源码中我们配置的规则解析的实现。 在上一篇文章末尾&#xff0c;在 IDE 中&#x…