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

news2024/9/20 22:39:10

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

字符数组的存储方式

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

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.coloradmin.cn/o/2070297.html

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

相关文章

老师怎样分班更便捷?

随着新学期的钟声敲响,老师们又迎来了一年中最繁忙的时刻。开学之初,除了要处理日常的教学事务,老师们还肩负着一项重要任务——给新生进行分班。 其实老师们完全可以不必那么劳累。在这个科技日新月异的时代,有许多工具可以帮助老…

计算机毕业设计选题推荐-高中素质评价档案系统-Java/Python项目实战

✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

SOMEIP_ETS_061: Sending_two_SOMEIP_Messages_in_a_row

测试目的: 验证设备(DUT)能够处理在单个UDP数据包中发送的多个SOME/IP消息,并对所有这些SOME/IP消息给出正确的响应。 描述 本测试用例旨在检查DUT在接收到一个包含多个SOME/IP消息的UDP数据包时,是否能够对所有包含…

如何使用MQTT订阅摄像机/NVR/DVR的AI报警

H5S内置MQTT服务,并把设备报警默认推送到MQTT服务器上,进入 设置-》协议-》MQTT配置MQTT服务参数,配置后需要重启生效。 MQTT开启后,就可以使用第三方MQTT客户端订阅事件,以下以MQTTX( https://mqttx.app/ )为例。 链…

深入解析css-学习小结

绪论 盒模型 层叠 优先级 继承 层叠 层叠指规则冲突时,如何选择规则。规则冲突解决顺序: 样式表来源 用户代理样式 用户代理样式:浏览器默认样式 作者样式表:你自己写的css样式 作者样式表会覆盖用户代理样式,因…

宅家必备神器!远程控制软件,让你随时随地掌控一切

在数字化时代,远程控制软件已经成为我们日常生活和工作中不可或缺的工具。今天,我将分享五款我使用过的远程控制软件的使用感受,希望大家能够选择到一款适合自己的远控工具: 一、向日葵远程控制 直通车(粘贴到浏览器…

基于xr-frame实现微信小程序的图片扫描识别AR功能(含源码)

前言 xr-frame是一套小程序官方提供的XR/3D应用解决方案,基于混合方案实现,性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准。xr-frame在基础库v2.32.0开始基本稳定,发布为正式版,但仍有一些功能还在开发&#…

NWM口罩佩戴检测算法,浅析口罩佩戴检测从源码到实际应用的全面指南

一、背景 随着新冠疫情的全球蔓延,佩戴口罩成为了预防病毒传播的重要措施。然而,随着疫情的持续,社会上仍存在不少未佩戴口罩的行为,这给公共健康带来了巨大的风险。在这样的背景下,基于计算机视觉的口罩检测算法应运…

ML307R_APP_DEMO_SDK TCP/UDP使用介绍

ML307R_APP_DEMO_SDK是在ML307R_OpenCPU_Standard_SDK标准代码基础上,新增了面向用户APP层的demo示例,与标准代码中examples的示例代码不同,app_demo实现了联网自动化,数据透传,各功能可独立自动运行,并对用…

【TB作品】TM1637芯片数码管,PIC16单片机驱动显示,Proteus仿真

文章目录 效果模块芯片介绍code 效果 只能是共阳数码管: 模块 芯片介绍 TM1637芯片是一种常用于LED数码管显示控制的驱动芯片,下面是各引脚的详细说明: DIO (Data Input/Output) - 管脚号: 17 功能: 串行数据输入/输出,DIO引脚…

JVM经典的垃圾收集器

经典垃圾收集器 目录 Serial收集器ParNew收集器Parallel Scavenge收集器 Serial Old收集器Parallel Old收集器CMS 收集器Garbage First收集器 以上是经典收集器的关系图 Serial收集器 Serial收集器是一个单线程工作的收集器,但它的“单线程”的意义并不仅仅是说…

『功能项目』禁止射线穿透行为【06】

我们打开上一篇04禁止射线穿透行为项目, 本章要做的事情是在Unity编辑器中添加 新输入系统 实现主角在场景中鼠标右键可以使主角 转向。 本次项目需要让Unity引擎重新启动所以先保存当前项目 再次打开项目后, 修改为Both 点击Apply前注意要先保存项目&a…

数据结构——树的三种表示方法

目录 引言 树 1.树的定义 2.树的基本概念 3.树的表示方式 (1)双亲表示法 (2)孩子表示法 (3)左孩子右兄弟表示法 (4)树的应用 结束语 引言 在学习完栈和队列的之后后,我们接下来学习新的数据结构——树。 树 1.树的定义 树是一种非线性数据结构&#x…

JavaScript 数据结构 ==== 二叉树

目录 二叉树 结构 二叉树和二叉搜索树介绍 1.创建树 2.插入一个键 3.树的遍历 中序排序 先序遍历 后序遍历 4.搜索树中的值 5.删除节点 二叉树 在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtre…

Docker Desktop镜像路径修改一直报错

一 点击Apply & Restart报错 [Window Title] Docker Desktop[Main Instruction] Error migrating WSL disk[Content] An error occurred while migrating the Docker Desktop WSL data disk to its new location:moving disk file: rename C:\Users\Lenovo\AppData\Local\D…

Linux 进程 | 进程地址空间

文章目录 进程地址空间程序地址空间进程地址空间 进程地址空间 程序地址空间 地址空间一共有如下的几个区域,从下到上地址逐渐增加,其中栈区的空间是从上往下使用,即从高地址往低地址增长;堆区的空间是从下往上使用,…

简单实现进度条效果(vue2)

如果用echarts或者其他图表来写个进度条有点大材小用&#xff0c;所以直接简单html、js写一下就可以&#xff1b; 以下代码基于vue2&#xff0c; 部分代码来自国内直连GPT/Claude镜像站 <template><div class"progress-container"><div class"p…

aosp源码导入android studio无法跳转-学员答疑

背景&#xff1a; 在学习framework入门课时候&#xff0c;一个很重要环节就是导入aosp的源码到android studio&#xff0c;这样有了IDE之后开发起来就很方便了&#xff0c;但是很多学员朋友对编译出来的ipr&#xff0c;iml文件改造不知道该怎么搞&#xff0c;导致android stud…

mysql 开启binlog并设置

打开my.cnf 文件&#xff0c;Linux系统文件默认位置为 /etc目录下&#xff0c;若不存在&#xff0c;可以使用下述命令查询 find / -name my.cnf修改my.cnf文件&#xff0c;开启binlog mysqld 模块下添加以下内容 server_id1 #给当前mysql机器设置一个id log-binmysql_bin …

分布式事务理论和解决方案

分布式事务理论 business&#xff08;下单&#xff09;远程调用库存&#xff08;storage&#xff09;,保存订单&#xff08;order&#xff09;&#xff0c;扣减积分&#xff08;account&#xff09;&#xff0c;只有这三个步骤全部成功&#xff0c;我们的下订单才算成功。 如果…