【JVM】剖析字符串与数组的底层实现(一)

embedded/2024/9/20 5:54:21/ 标签: jvm, java, hotspot, OpenJDK, String

剖析字符串与数组的底层实现

字符数组的存储方式

在这里插入图片描述
在这里插入图片描述

JVM有三种模型:

  • 1.Oop模型:Java对象对应的C++对象
  • 2.Klass模型:Java类在JVM对应的C++对象
  • 3.handle模型

字符串常量池

在这里插入图片描述

String Pool,但是JVM中对应的类是StringTable,底层实现是一个hashtable,如代码所示

JVM有三种常量池:

  • 1.静态常量池(通过字节码方式查到地引用都是间接引用)
  • 2.运行时常量池
  • 3.字符串常量池->StringTable
    key生成规则->String内容+长度生成哈希值,然后将hash值取模转为key
    value生成规则->将Java地String类的实例InstanceOopDesc封装成HashtableEntry

字符串常量池位置

JDK1.6及之前:有永久代,运行时常量池在永久代,运行时常量池包含字符串常量池,Perm区域只有4m,一旦常量池大量使用intern很容易发生永久代的OOM
JDK1.7:有永久代,但已经逐步"去永久代",字符串常量池从永久代里的运行时常量池分离到堆里
JDK1.8及之后,无永久代,运行时常量池在元空间,字符串常量池依然在堆里

字符串常量池的设计思想:

  • 1.字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序地性能
  • 2.JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
    2.1 为字符串开辟一个字符串常量池,类似于缓存区
    2.2 创建字符串常量时,首先查询字符串常量池是否存在该字符串
    2.3 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

字符串常量池设计原理

在这里插入图片描述

字符串常量池底层时HotSpot的C++实现的。底层类似一个Hashtable,保存的本质上是字符串对象的引用,来看一道比较常见的案例,图中的代码创建了多少个String对象
// JDK6:false 创建了6个对象
// JDK7及以上:true 创建了5个对象

为什么输出会有这些变化呢?主要还是字符串从永久代中脱离、移入堆区的原因,intern()方法也相应发生了变化
同时也解释了JDK1.6中字符串溢出会抛出OutOfMemoryError:PermGen Space.而在JDK1.7及以上版本会抛出OutOfMemoryError:Java heap space

在这里插入图片描述

在JDK1.6中,调用intern()首先会在字符串池中寻找equals相等的字符串,加入字符串存在就返回该字符串在字符串池中的引用,假如字符串不存在,虚拟机会重新在永久代上创建一个实例,将StringTable的一个表项指向这个新创建的实例
在这里插入图片描述

在JDK1.7(及以上版本)中,由于字符串池不在永久代了,intern()做了一些修改,更方便地利用堆中的对象。字符串存在时和JDK1.6一样,但是字符串不存在时不再需要重新创建实例,可以直接指向堆上的实例

我们再来看一个例子
在这里插入图片描述
JDK1.7以上:false true
JDK1.6: false false

在这里插入图片描述

jdk1.6代码图
在JDK1.6中上述的所有打印都是false,因为jdk6的常量池是放在Perm区中的,Perm区和正常的Java Heap区域是完全分开的。如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而new出来的String对象是放在Java Heap区域。所以拿一个Java Heap区域的对象地址和字符串常量池的对象地址进行比较比较肯定是不相同的,即使调用String.inern方法也是没有关系的

在这里插入图片描述

jdk1.7代码图
这里要明确一点的是,在Jdk6以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用intern是会直接产生java.lang.OutOfMemoryError:PermGen Space错误的。Perm区域太小是一个主要原因,当然在1.8中已经直接取消了Perm区域,而新建立了一个元区域。应该是jdk开发者认为Perm区域已经不适合现在Java的发展了。
正是因为字符串常量池移动到Java Heap区域后,再看下面解释。

  • 1.先看s3和s4字符串,String s3 = new String(“1”) + new String(“1”);这句代码中现在生成了两个对象,一个是字符串常量池中的"1",另一个是Java Heap中的s3引用指向的对象。中间还有2个匿名的new String(“1”),不去讨论他们,此时s3引用对象内容是"11",但此时常量池中是没有"11"对象的
  • 2.接下来,s3.intern(); 这一句代码,是将s3中的"11"字符串放入String常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟jdk6图中所示的一样,再常量池中生成一个"11"的对象,关键点是jdk7中常量池不在Perm区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。也就是说引用地址是相同的
  • 3.最后String s4 = “11”;这句代码中"11"是显式声明的,因此会直接区常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向s3引用对象的一个引用。所以s4引用就指向和s3一样了,因此最后的比较s3 == s4 是true
  • 4.再看s和s2对象,String s = new String(“1”);第一句代码,生成了2个对象。常量池中的"1"和Java Heap中的字符串对象 s.intern()这一句是s对象区常量池中寻找后发现"1"已经再常量池里了
  • 5.接下来String s2 = “1”;这句代码是生成一个s2的引用指向常量池中的"1"对象。结果就是s和s2引用地址明显不同

例子二
在这里插入图片描述

JDK1.7以上:false false
JDK1.6 false false

在这里插入图片描述

  • 1.代码一和代码二的改变就是s3.intern的顺序是放在了String s4 = "11"后了,这样,首先执行String s4 = “11”;声明s4的时候常量池中是不存在"11"对象的。执行完毕后"11"对象是s4声明产生的对象。然后再执行s3.intern时,发现常量池中"11"对象已经存在了,因此s3和s4的引用时不同的
  • 2.s和s2代码中,s.intern()这一句往后放也不会有什么影响了,因为对象池中执行第一句代码String s = new String(“1”);的时候已经生成"1"对象的了,下边的s2声明都是直接从常量池中取地址引用的,s和s2的引用地址是不会相等的

key的生成方式

在这里插入图片描述

1.通过String的内容 + 长度生成hash值
2.将hash值转为key
在这里插入图片描述

hash生成方式

在这里插入图片描述

通过hash计算索引

value的生成方式

将Java的String类的实例InstanceOopDesc封装成HashtableEntry
在这里插入图片描述

在这里插入图片描述


http://www.ppmy.cn/embedded/100140.html

相关文章

django对象报错object has no attribute ‘count‘

提问 我正在学习django/python,并被一个问题给卡住了。 我有一个视图:create_document.py,我想在其中计算模型类NameDetails中的名字细节数量。 但我不知道正确的写法! 这是我models.py的代码: class NameDetails…

aspose-words在指定位置添加ole附件并指定默认打开方式

aspose-words版本:21.1 java:1.8 目标: 在html中找到a标签,aspose-words组件会将其转换为超链接标签HYPERLINK,找到超链接的文职并替换为相应的附件。 public void insertOleObjectAsIconUsingStream() throws Excep…

速盾:cdn能防ip追踪吗?

CDN,即内容分发网络(Content Delivery Network),是一种通过网络分布在多个地理位置的服务器集群来提供高效内容传输服务的技术。CDN的主要目的是通过就近提供内容来加快网站的加载速度,并减少因服务器过载而导致的延迟…

【SCI/EI/SCOPUS/CNKI】第三届先进材料与装备制造国际会议(AMEM2024)

会议日期:2024年12月28-30日 会议地点:中国-云南省-昆明市 会议官网:https://www.iaast.cn/meet/home/Bx93wRT 出版检索:EI、Scopus等数据库收录 【主办单位】 国际应用科学与技术协会(IAAST) 【主讲嘉宾】 【论文出版与检…

记一次hivemetastore启动报错

1,启动hivemetastore后报错日志 2,排查lib下的mysql的驱动也在, 这里和mysql的驱动大小一样 3,把hive-site.xml中无关的配置都删掉,重启metastore还是报错 4,最后排查,这个节点rpm部署了hive…

《机器学习》—— AUC评估指标

文章目录 一、什么是AUC?1、什么是ROC曲线?2、ROC曲线的绘制 二、如何计算AUC的值三、代码实现AUC值的计算四、AUC的优缺点 一、什么是AUC? 机器学习中的AUC(Area Under the Curve)是一个重要的评估指标,特…

信息学奥赛知识点(八)----计算机网络

一、网络的定义 所谓计算机网络,就是利用通信线路和设备,把分布在不同地理位置上的多台计算机连接起来。 计算机网络是现代通信技术与计算机技术相结合的产物。 网络中的计算机与计算机之间的通信依靠协议进行。协议是计算机收、发数据的规则。TCP/IP…

Pytorch cat()与stack()函数详解

torch.cat() cat为concatenate的缩写,意思为拼接,torch.cat()函数一般是用于张量拼接使用的 cat(tensors: Union[Tuple[Tensor, ...], List[Tensor]], dim: _int 0, *, out: Optional[Tensor] None) -> Tensor: 可以看到cat()函数的参数&#xf…

Django后端架构开发:构建在线云媒资系统思路解析

Django后端架构开发:构建在线云媒资系统思路解析 📈 Django 打造在线云媒资系统的思路与实现 构建一个在线云媒资系统涉及多方面的技术实现,尤其是Django框架的应用在后端架构中扮演了关键角色。这个系统的核心在于高效的API组件开发、云空…

【TCP】连接管理:三次握手和四次挥手

连接管理 建立连接:三次握手断开连接:四次挥手 网络中的握手/挥手,就是发送不携带业务数据(没有载荷,只有报头)的数据包,但是能起到“打招呼”这样的效果。次数就是指网络通信的次数。 建立连…

软件开发者的首选:最佳Bug测试工具Top 10

本篇文章介绍了以下软件bug测试管理工具:PingCode、Worktile、Test360、禅道、码云Gitee、优云测试、Jira、GitHub、Axosoft、Bugzilla。 在开发过程中,Bug的管理往往是最让人头疼的问题之一。小问题积累起来不仅会拖延项目进度,还可能影响到…

Java笔试面试题AI答之线程(18)

文章目录 103. 简述Java线程池中队列常用类型有哪些 ?1. ArrayBlockingQueue2. LinkedBlockingQueue3. SynchronousQueue4. PriorityBlockingQueue5. DelayQueue 104. 简述线程安全需要保证几个基本特征?105. 简述线程池原理以及核心参数 ?线…

Linux发送邮件:如何配置SMTP服务器发信?

linux发送邮件至多个收件人的方法?如何用Linux命令? 在Linux系统中,邮件发送是一个常见且重要的功能,无论是用于系统监控通知还是日常通信。AokSend将详细介绍如何在Linux环境下配置SMTP服务器,以确保您的邮件发送既高…

揭露 Sapiens:未来以人为中心的视觉任务

Sapiens | Meta Meta Reality Labs 隆重推出 Sapiens,这是一个尖端的模型系列,专为四种以人为中心的基本视觉任务而设计:二维姿态估计、身体部位分割、深度估计和表面法线预测。 我们的 Sapiens 模型可无缝处理 1K 高分辨率推理&#xff0c…

开源技术社区/论坛平台Paicoding

Paicoding是一款面向开发者的学习与实践平台,它基于多种主流技术栈构建,为开发者提供了丰富的学习资源和实战项目。 开源地址:paicoding: ⭐️一款好用又强大的开源社区,基于 Spring Boot、MyBatis-Plus、MySQL、Redis、ElasticSe…

【C++】深入理解C++继承:从基础到高级

C继承 什么是继承?继承的类型继承中的构造函数与析构函数继承中的覆盖与隐藏虚继承与虚函数继承的最佳实践总结 继承是C中面向对象编程(OOP)的核心概念之一。通过继承,开发者可以创建一个新的类,该类不仅可以拥有自己独…

zustand/middleware/immer中的immer怎么用

在解释这段代码之前,我们需要先了解一些背景知识。首先,zustand 是一个用于状态管理的小型、可扩展的库,它鼓励使用单一状态树来管理应用中的所有状态。而 immer 是一个库,用于创建不可变数据的简化方法, 它允许你以可…

建造者模式 和 外观模式

这两种模式很像, 都是将一个复杂的流程统一用一个方法进行包装, 方便外界使用. 建造者模式更像是 外观模式的一种特里, 只对一个类的复杂初始化流程进行包装 建造者模式 简介: 就是一个类的构造方法可能很复杂, 由于系统的限制等原因, 可能很多初始化逻辑不能放在构造函数里,…

语音控制开关的语音识别ic芯片方案

语音控制开关是一种基于语音识别技术的设备,它通过内置的语音识别芯片,将用户的语音指令转化为电信号,从而实现对设备的控制。例如在智能家居设备上的应用,通常需要连接到家庭的Wi-Fi网络上,以便与智能手机或智能音箱等…

AR 眼镜之-系统应用音效-实现方案

目录 📂 前言 AR 眼镜系统版本 系统应用音效 1. 🔱 技术方案 1.1 技术方案概述 1.2 实现方案 1)初始化 2)播放音效 3)释放资源 2. 💠 播放音效 2.1 静音不播放 2.2 获取音效默认音量 3. ⚛️ …