当前位置: 首页 > news >正文

String类的底层原理和版本演变

1 String类的底层演变

  (1) JDK8以及之前版本

(2)JDK9以及之后版本

  ```java

  JDK8的字符串存储在char类型的数组里面,在java中,一个char类型占两个字节。但是很多时候,一个字符只需要一个字节就可存储,比如各种字母什么的,两个字节存储势必会浪费空间,JDK9的一个优化就在这,内存的优化,所以JDK9之后字符串改成byte类型数组进行存储。

  private final byte coder;

  在JDK9的String类中,新增了一个属性coder,它是一个编码格式的标识,使用LATIN1还是UTF16,这个是在String生成的时候自动确定的,如果字符串中都是能用LATIN1编码表示,那coder的值就是0,否则就是UTF16编码,coder的值就是1。

  可以看到JDK9在这方面的优化,在较多情况下不包含那些奇奇怪怪的字符的时候,足以应付,而这个空间却小了1byte,实现了String空间的压缩。

  2 String常量池的演变

  2.1 StringTable变化

String 的 StringPool是一个固定大小的 Hashtable。在jdk6中,StringTable的长度固定为1009。如果放进 StringPool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用 intern() 时性能会大幅下降。从jdk7起,StringTable的长度默认值是60013。使用-XX:StringTableSize可设置StringTable的长度。    在jdk8之前,对StringTableSize的设置没有最小限制。jdk8开始,StringTable可设置的最小值是1009。验证:通过 jps 命令查看进程号使用 jinfo -flag StringTableSize 进程号 查看StringTable大小    
```

  2.2 内存位置变化

Java6及以前,字符串常量池存放在永久代。Java7开始,字符串常量池的位置调整到Java堆内。
所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样在进行调优应用时仅需要调整堆大小就可以了。
```

  官网说明

  https://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

  JDK6环境下测试:

/*jdk6中,修改JVM内存大小:-XX:PermSize=6m -XX:MaxPermSize=6m -Xms6m -Xmx6m*/publicclassStringTableTest{publicstaticvoidmain(String[] args){Set<String> set =newHashSet<String>();int i=0;while(true){set.add(String.valueOf(i++).intern());}}}执行结果异常信息:Exception in thread "main" java.lang.OutOfMemoryError:PermGen spaceat java.lang.String.intern(NativeMethod)
```

  JDK7环境下测试:

/*jdk7中,修改JVM内存大小:-XX:PermSize=6m -XX:MaxPermSize=6m -Xms6m -Xmx6m -XX:-UseGCOverheadLimit*/publicclassStringTableTest{publicstaticvoidmain(String[] args){Set<String> set =newHashSet<String>();int i=0;while(true){set.add(String.valueOf(i++).intern());}}}执行结果异常信息:Exception in thread "main" java.lang.OutOfMemoryError:Java heap spaceat java.lang.Integer.toString(Integer.java:331)at java.lang.String.valueOf(String.java:2954)at StringTableTest.main(StringTableTest.java:14)
```

  3 String的拼接原理

  3.1 拼接原理

  源代码:

publicstaticvoidmain(String[] args){String s1 ="hello";String s2 ="world";String s3 = s1+s2;System.out.println(s3);}```

  使用 JDK8 编译后字节码:

0 ldc #2<hello>2 astore_13 ldc #3<world>5 astore_26new #4<java/lang/StringBuilder>9 dup
10 invokespecial #5<java/lang/StringBuilder.<init>>13 aload_1
14 invokevirtual #6<java/lang/StringBuilder.append>17 aload_2
18 invokevirtual #6<java/lang/StringBuilder.append>21 invokevirtual #7<java/lang/StringBuilder.toString>24 astore_3
25 getstatic #8<java/lang/System.out>28 aload_3
29 invokevirtual #9<java/io/PrintStream.println>32return
```

  使用 JDK9 编译后字节码:

0 ldc #2<hello>2 astore_13 ldc #3<world>5 astore_26 aload_17 aload_28 invokedynamic #4<makeConcatWithConstants,BootstrapMethods #0>13 astore_3
14 getstatic #5<java/lang/System.out>17 aload_3
18 invokevirtual #6<java/io/PrintStream.println>21return
```

  结论:

  ```java

  JDK8及之前,字符串变量的拼接,底层使用的是StringBuilder对象,利用append方法进行拼接。

  (注:jdk1.4之前使用StringBuffer)

  JDK9以后的编译器已经改成使用动态指令invokedynamic,

  调用StringConcatFactory.makeConcatWithConstants方法进行字符串拼接优化。

  ```

  3.2 核心方法

makeConcatWithConstants方法在StringConcatFactory类中定义。makeConcatWithConstants内部调用了doStringConcat,而doStringConcat方法则调用了generate方法来生成MethodHandle;generate根据不同的STRATEGY来生成MethodHandle,这些STRATEGY(策略)有BC_SB(等价于JDK8的优化方式)BC_SB_SIZEDBC_SB_SIZED_EXACTMH_SB_SIZEDMH_SB_SIZED_EXACTMH_INLINE_SIZED_EXACT(默认)前五种策略本质还是用StringBuilder的实现,而默认的策略MH_INLINE_SIZED_EXACT是直接使用字节数组来操作,并且字节数组长度预先计算好,可以减少字符串复制操作。可以通过添加JVM参数来改变默认的策略,例如将策略改为BC_SB-Djava.lang.invoke.stringConcat=BC_SB-Djava.lang.invoke.stringConcat.debug=true```

  源码:

  ==makeConcatWithConstants内部调用了doStringConcat方法==

  ==doStringConcat方法则调用了generate方法来生成MethodHandle==

  ==generate根据不同的STRATEGY来生成MethodHandle==

  ==这些STRATEGY(策略)分别是==

privateenumStrategy{/*** Bytecode generator, calling into {@linkjava.lang.StringBuilder}.*/BC_SB,/*** Bytecode generator, calling into {@linkjava.lang.StringBuilder};* but trying to estimate the required storage.*/BC_SB_SIZED,/*** Bytecode generator, calling into {@linkjava.lang.StringBuilder};* but computing the required storage exactly.*/BC_SB_SIZED_EXACT,/*** MethodHandle-based generator, that in the end calls into {@linkjava.lang.StringBuilder}.* This strategy also tries to estimate the required storage.*/MH_SB_SIZED,/*** MethodHandle-based generator, that in the end calls into {@linkjava.lang.StringBuilder}.* This strategy also estimate the required storage exactly.*/MH_SB_SIZED_EXACT,/*** MethodHandle-based generator, that constructs its own byte[] array from* the arguments. It computes the required storage exactly.*/MH_INLINE_SIZED_EXACT}
```

  ==默认的策略MH_INLINE_SIZED_EXACT==

  3.3 常见笔试题

/*产生2个字符串对象:字符串常量池中一个,堆内存中一个。
*/String s =newString("abc");/*产生1个字符串对象:常量池中的"abc"。代码在编译阶段会优化为 String s = "abc";
*/String s ="a"+"b"+"c";/*5个字符串对象常量池:"a", "b"堆内存:new方式的"a",new方式的"b",new方式的"ab"注意:常量池中不会产生"ab"
*/String s =newString("a")+newString("b");/*
jdk8及之前创建3个字符串对象:常量池: "c" , "ab"堆中: new "abc"jdk9之后创建2个字符串对象:常量池: "c"堆中: new "abc"
*/String s1 ="c";String s2 ="a"+"b"+s1;```

  4 intern()方法的演变

  4.1 intern()方法调用区别

publicclassStringDemo5{publicstaticvoidmain(String[] args){String s1 =newString("ab");String s2 ="ab";System.out.println(s1==s2);//fasle//intern()方法从常量池中取出"ab"对象String s1 =newString("ab").intern();String s2 ="ab";System.out.println(s1==s2);//true/*从常量池中取出和s1内容相同的"ab"对象,此时常量池中没有"ab"对象。如果常量池中没有该字符串对象:jdk6及之前,intern()方法会创建新的字符串对象,放入常量池并返回新的地址。jdk7及之后,intern()方法会将调用者对象的地址放入常量池,并返回调用者对象地址。*/String s1 =newString("a")+newString("b");s1.intern();String s2 ="ab";System.out.println(s1==s2);//jdk6 false;  jdk7之后true}}
```

  4.2 intern()方法总结

  ```java

  intern()方法将这个字符串对象尝试放入常量池中,并返回地址。

  jdk1.6中:

  如果池中有,则不会放入,返回已有的池中的对象的地址。

  如果池中没有,则把此对象重新创建一份,放入池中,并返回池中新的对象地址。

  jdk1.7起:

  如果池中有,则不会放入,返回已有的池中的对象的地址。

  如果池中没有,则把此对象的引用地址复制一份,放入池中,并返回池中的引用地址。

  ```

相关文章:

String类的底层原理和版本演变

1 String类的底层演变&#xff08;1&#xff09; JDK8以及之前版本 &#xff08;2&#xff09;JDK9以及之后版本 javaJDK8的字符串存储在char类型的数组里面&#xff0c;在java中&#xff0c;一个char类型占两个字节。但是很多时候&#xff0c;一个字符只需要一个字节就可存储&…...

软考高级信息系统项目管理师系列之二十三:项目采购管理

软考高级信息系统项目管理师系列之二十三:项目采购管理 一、项目采购管理内容整理二、项目采购管理1.采购的定义2.项目采购管理3.战略合作管理三、规划采购1.供应商管理2.采购需求与计划3.规划采购的输入、输出、工具和技术四、实施采购1.采购合同知识2.实施采购的输入、输出、…...

SpringMVC-0308

五、域对象共享数据0、三个域对象范围request&#xff1a;一次请求 第1&#xff5e;6都是向request共享session&#xff1a;一次会话&#xff08;浏览器开启到浏览器关闭&#xff0c;与服务器关闭无关&#xff0c;session有钝化和活化操作&#xff0c;可以持久化数据&#xff0…...

[数据结构]:14-选择排序(顺序表指针实现形式)(C语言实现)

目录 前言 已完成内容 选择排序实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-PSeqListFunction.cpp 04-SortCommon.cpp 05-SortFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容&#xff0c;除其中使用到C引用外&#xff0c;全为C语言代…...

基于C/C++综合训练 ----- 贪吃蛇

文章目录一、定义结构体对象二、游戏初始化1. 蛇初始化2. 食物初始化3. 围墙初始化4. 界面初始化三、逻辑编程1. 启动游戏2. 打印成绩3. main函数四、细节处理五、程序源码该篇环境为Visual Studio2022 游戏简述 &#xff1a;在控制终端绘画出一个矩阵表示游戏界面(围墙)&…...

Unity 混合操作(Blending)

渲染图形时&#xff0c;在执行所有着色器并应用所有纹理后&#xff0c;像素将写入到屏幕。这些像素与已有像素的组合方式由 Blend 命令控制。用于生成透明对象。《Unity Shader入门精要》大致解释&#xff1a;片元通过了模板测试和深度测试之后&#xff0c;会进行混合步骤。如果…...

Hive建表高阶语句

CTAS -as select方式建表CREATE TABLE ctas_employee as SELECT * FROM employee;CTE (CTAS with Common Table Expression)CREATE TABLE cte_employee AS WITH r1 AS (SELECT name FROM r2 WHERE name Michael), r2 AS (SELECT name FROM employee WHERE gender Male), r3 …...

面向新时代,海泰方圆战略升级!“1465”隆重发布!

过去四年&#xff0c;海泰方圆“1344”战略一直在引领公司前行&#xff0c;搭建了非常坚实的战略框架基座&#xff0c;并推动全员在实践和行动中达成深度共识。 “1344”战略 1个定位&#xff0c;代表着当前机构用户的一组共性需求&#xff0c;密码安全数据治理信创工程。 3…...

带你感受一次JVM调优实战

本文分成两部分&#xff0c;先了解理论&#xff0c;然后再进行实战。 理论篇 1.1 调优目标 JVM调优的两大目标是&#xff1a; 提高应用程序的性能和吞吐量&#xff1a; 通过优化JVM的垃圾回收机制、调整线程池大小和优化代码&#xff0c;可以提高应用程序的性能和吞吐量。…...

ALG和STUN

目录 ALG 应用层网关讲解 Client1使用FTP主动模式建立FTP Client1使用FTP被动模式建立FTP STUN讲解 ALG 应用层网关讲解 用来替换应用层信息 Client1使用FTP主动模式建立FTP 主动模式&#xff1a;服务器收到客户端发来的请求FTP的地址和端口 服务器使用20端口直接向客户端建…...

原生HTML放大镜

该放大区域用背景图片放大 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compat…...

C++——模板

文章目录1 泛型编程2 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的实例化2.3.1 隐式实例化2.3.1.1 定义2.3.1.2 代码演示2.3.1.3 运行结果2.3.1.4 缺点2.3.2 显式实例化2.3.2.1 格式2.3.2.2 代码演示2.3.2.3 运行结果2.4 模板参数的匹配原则2.4.12.4.22.4.33 类模板…...

Chapter2.1:线性表基础

该系列属于计算机基础系列中的《数据结构基础》子系列&#xff0c;参考书《数据结构考研复习指导》(王道论坛 组编)&#xff0c;完整内容请阅读原书。 1.线性表的定义和基本操作 1.1 线性表的定义 线性表是具有相同数据类型的n(n≥0)n(n≥0)n(n≥0)个数据元素的有限序列&…...

Spring源码解析-Spring 循环依赖

Spring源码解析简图&#xff1a; Spring 如何解决循环依赖&#xff0c;⽹上的资料很多&#xff0c;但是感觉写得好的极少&#xff0c;特别是源码解读⽅⾯&#xff0c;我就⾃⼰单独出⼀ 篇&#xff0c;这篇⽂章绝对肝&#xff01; 文章目录&#xff1a; 一. 基础知识 1.1 什么…...

从零开始学架构——架构设计的目的

软件架构的历史背景 软件架构真正流行是从20世纪90年代开始的&#xff0c;由于在Rational和Microsoft内部的相关活动&#xff0c;软件架构的概念开始越来越流行。 卡内基梅隆高校的玛丽肖(Mary Shaw)和戴维加兰 (David Garlan)对软件架构做了许多讨论,他们在 1994 年的一篇文章…...

Python 异步: 异步生成器(16)

动动发财的小手&#xff0c;点个赞吧&#xff01; 生成器是 Python 的基本组成部分。生成器是一个至少有一个“yield”表达式的函数。它们是可以暂停和恢复的函数&#xff0c;就像协程一样。 实际上&#xff0c;Python 协程是 Python 生成器的扩展。Asyncio 允许我们开发异步生…...

.net6 web api使用EF Core,根据model类自动生成表

1.安装EF Core和mysql数据库的nuget包 Microsoft.EntityFrameworkCore Pomelo.EntityFrameworkCore.MySql 2.创建models文件夹&#xff0c;在文件夹下创建实体类 public class Users{public int Id { get; set; }[Column(TypeName "varchar(200)"), Required]publ…...

计算机科学导论笔记(五)

目录 七、操作系统 7.1 引言 7.1.1 操纵系统 7.1.2 自举过程 7.2 演化 7.3 组成部分 7.3.1 用户界面 7.3.2 内存管理器 7.3.3 进程管理器 7.3.4 设备管理器 7.3.5 文件管理器 7.4 操作系统 7.4.1 UNIX 7.4.2 Linux 7.4.3 Windows 七、操作系统 7.1 引言 计算机…...

通过命令打Java可执行jar包

文章目录1.背景2.操作步骤2.1. 准备好java源文件2.2 确认java源文件中是否有包名2.3 编译java文件2.4 初步打包2.5 解压jar包&#xff0c;得到MANIFEST.MF文件2.6 修改MANIFEST.MF文件2.7 再次打包3.验证4.打包参数参考5.参考文章1.背景 今天&#xff0c;无意中翻出了N年之前年…...

java基础系列(九) 接口和抽象类

一. 接口 简单的说&#xff0c;接口就是一种被规范的标准&#xff0c;只要符合这个标准都可以通用&#xff0c;接口的表现 在于对行为的抽象. 1.1 创建接口的格式 格式1: public interface 接口名 格式2: interface 接口名 1.2 在JDK1.8之后, 在接口中可以定义实现的方…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...