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类的底层演变(1) JDK8以及之前版本 (2)JDK9以及之后版本 javaJDK8的字符串存储在char类型的数组里面,在java中,一个char类型占两个字节。但是很多时候,一个字符只需要一个字节就可存储&…...
软考高级信息系统项目管理师系列之二十三:项目采购管理
软考高级信息系统项目管理师系列之二十三:项目采购管理 一、项目采购管理内容整理二、项目采购管理1.采购的定义2.项目采购管理3.战略合作管理三、规划采购1.供应商管理2.采购需求与计划3.规划采购的输入、输出、工具和技术四、实施采购1.采购合同知识2.实施采购的输入、输出、…...
SpringMVC-0308
五、域对象共享数据0、三个域对象范围request:一次请求 第1~6都是向request共享session:一次会话(浏览器开启到浏览器关闭,与服务器关闭无关,session有钝化和活化操作,可以持久化数据࿰…...

[数据结构]:14-选择排序(顺序表指针实现形式)(C语言实现)
目录 前言 已完成内容 选择排序实现 01-开发环境 02-文件布局 03-代码 01-主函数 02-头文件 03-PSeqListFunction.cpp 04-SortCommon.cpp 05-SortFunction.cpp 结语 前言 此专栏包含408考研数据结构全部内容,除其中使用到C引用外,全为C语言代…...

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

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

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

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

ALG和STUN
目录 ALG 应用层网关讲解 Client1使用FTP主动模式建立FTP Client1使用FTP被动模式建立FTP STUN讲解 ALG 应用层网关讲解 用来替换应用层信息 Client1使用FTP主动模式建立FTP 主动模式:服务器收到客户端发来的请求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:线性表基础
该系列属于计算机基础系列中的《数据结构基础》子系列,参考书《数据结构考研复习指导》(王道论坛 组编),完整内容请阅读原书。 1.线性表的定义和基本操作 1.1 线性表的定义 线性表是具有相同数据类型的n(n≥0)n(n≥0)n(n≥0)个数据元素的有限序列&…...

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

从零开始学架构——架构设计的目的
软件架构的历史背景 软件架构真正流行是从20世纪90年代开始的,由于在Rational和Microsoft内部的相关活动,软件架构的概念开始越来越流行。 卡内基梅隆高校的玛丽肖(Mary Shaw)和戴维加兰 (David Garlan)对软件架构做了许多讨论,他们在 1994 年的一篇文章…...
Python 异步: 异步生成器(16)
动动发财的小手,点个赞吧! 生成器是 Python 的基本组成部分。生成器是一个至少有一个“yield”表达式的函数。它们是可以暂停和恢复的函数,就像协程一样。 实际上,Python 协程是 Python 生成器的扩展。Asyncio 允许我们开发异步生…...

.net6 web api使用EF Core,根据model类自动生成表
1.安装EF Core和mysql数据库的nuget包 Microsoft.EntityFrameworkCore Pomelo.EntityFrameworkCore.MySql 2.创建models文件夹,在文件夹下创建实体类 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包,得到MANIFEST.MF文件2.6 修改MANIFEST.MF文件2.7 再次打包3.验证4.打包参数参考5.参考文章1.背景 今天,无意中翻出了N年之前年…...
java基础系列(九) 接口和抽象类
一. 接口 简单的说,接口就是一种被规范的标准,只要符合这个标准都可以通用,接口的表现 在于对行为的抽象. 1.1 创建接口的格式 格式1: public interface 接口名 格式2: interface 接口名 1.2 在JDK1.8之后, 在接口中可以定义实现的方…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...