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

news2024/10/3 8:31:13

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 space
    at 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 space
    at 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_1
 3 ldc #3<world>5 astore_2
 6new #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_1
 3 ldc #3<world>5 astore_2
 6 aload_1
 7 aload_2
 8 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_SIZED
        BC_SB_SIZED_EXACT
        MH_SB_SIZED
        MH_SB_SIZED_EXACT
        MH_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起:

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

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

  ```

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

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

相关文章

QGC UDP 建立通讯连接

QGC关于通信连接建立部分的程序比较复杂&#xff0c;但感觉写的很厉害&#xff0c;通过读代码把自己的一点理解记录下来。收集接收空地通信管理空地通信的管理在“应用程序设置->通讯连接”中进行通信连接创建&#xff0c;连接和断开。通讯连接添加完成后点击“连接”按钮&a…

SpringBoot+Spring常用注解总结

1. SpringBootApplication 这里先单独拎出SpringBootApplication 注解说一下&#xff0c;虽然我们一般不会主动去使用它。 SpringBootApplication public class SpringSecurityJwtGuideApplication {public static void main(java.lang.String[] args) {SpringApplication.ru…

携程 x TiDB丨应对全球业务海量数据增长,一栈式 HTAP 实现架构革新

随着新冠病毒疫情的缓解和控制&#xff0c;全球旅游业逐渐开始重新复苏。尤其在一些度假胜地&#xff0c;游客数量已经恢复到疫情前的水平。 携程作为全球领先的一站式旅行平台&#xff0c;旗下拥有携程旅行网、去哪儿网、Skyscanner 等品牌。携程旅行网向超过 9000 万会员提供…

微服务进阶之 SpringCloud Alibaba

文章目录微服务进阶&#x1f353;SpringCloud 有何劣势&#xff1f;&#x1f353;SpringCloud Alibaba 提供了什么&#xff1f;提示&#xff1a;以下是本篇文章正文内容&#xff0c;SpringCloud 系列学习将会持续更新 微服务进阶 &#x1f353;SpringCloud 有何劣势&#xff1…

Spring的概述及Spring6快速入门

Spring6 1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单…

Git图解-远程仓库-Gitee

目录 一、前言 二、远程仓库介绍 三、Gitee&#xff08;码云&#xff09;的使用 3.1 注册与登录 3.2 创建项目 3.3 项目初始化 3.4 添加团队成员 四、转视频版 一、前言 接着上篇&#xff1a; Git图解-常用命令操作-可视化 上篇已将Git可是化操作&#xff0c; 接下来&…

Spring整体架构包含哪些组件?

Spring是一个轻量级java开源框架。Spring是为了解决企业应用开发的复杂性而创建的&#xff0c;它使用基本的JavaBean来完成以前只可能由EJB完成的事情。 Spring的用途不仅限于服务器端的开发&#xff0c;从简单性、可测试性和松耦合的角度而言&#xff0c;任何java应用都可以从…

dolphinschedule使用shell任务结束状态研究

背景&#xff1a;配置的dolphin任务&#xff0c;使用的是shell&#xff0c;shell里包含了spark-submit 如下截图。 dolphin shell 介绍完毕&#xff0c;开始说明现象。 有天有人调整了集群的cdp配置&#xff0c;executor-cores max1 我之前这里写的是2&#xff0c;所以spark任…

如何简单的上手JavaFX17+?解决“缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序”等问题

前言 JavaFX是一个Java的一个ui库&#xff0c;但是自从java8以后&#xff0c;它很悲惨的被从OpenJdk中移除了&#xff0c;因此我们使用它来进行开发时&#xff0c;如果我们使用的是8以后的版本&#xff0c;经常会遇到一些问题。这里针对新手做一个简单的引导&#xff08;悲&am…

JDBC知识点全面总结2:JDBC实战编写CRUD

二.JDBC知识点全面总结1&#xff1a;JDBC实战编写CRUD 1.JDBC重要接口&#xff1f; 2.Driver和DriverMangement的关系&#xff1f; 3.JAVA与数据库连接 4.JAVA中使用statement来执行sql语句时&#xff0c;拼接字符串的sql注入问题&#xff1f; 5.使用preparedstatement进行…

【Microsoft】与 Bing AI 进行 ⌈狂飙⌋

&#x1f38a; 今天是3月8号&#xff0c;❤️农历二月十七&#xff0c;&#x1f495;祝广大女同胞们&#x1f469;女神节快乐&#x1f389;&#xff01;——以创作之名致敬女性开发者文章目录序言Ⅰ、Bing AI初体验Ⅱ、代码生成Ⅲ、生成图像Ⅳ、使用次数Ⅴ、总结序言 ​ 近期&…

数据结构与算法之《顺序表》

目录 1.什么是顺序表 顺序表的优势和缺点 顺序表预备知识 顺序表的代码实现 顺序表头部插入 顺序表的销毁 顺序表的头删 顺序表的尾删 顺序表的尾插 顺序表的任意位置插入 顺序表的查找 顺序表的打印 1.什么是顺序表 这篇文章我们来讲一下基础数据结构的顺序表&…

Input子系统(一)启动篇

代码路径 基于AndroidS&#xff08;12.0&#xff09;代码 system/core/libutils/Threads.cppframeworks/base/services- java/com/android/server/SystemServer.java- core- java/com/android/server/input/InputManagerService.java- jni/com_android_server_input_InputMan…

三步 让你的 vscode 自动编译ts文件

三步让你的 vscode 自动编译ts文件 TypeScript环境安装与如何在vscode实现自动编译ts文件? 文章目录三步让你的 vscode 自动编译ts文件前提条件环境安装自动编译运行监视任务时报错&#xff1f;前提条件 安装 node 环境 环境安装 tsc 作用&#xff1a;负责将ts 代码 转为 浏…

感知器算法

感知器算法原理说明 感知器是一种二分类的线性分类算法&#xff0c;其原理基于神经元的工作原理。感知器将输入数据通过加权求和的方式映射到一个输出&#xff0c;然后根据输出的结果进行分类。 具体来说&#xff0c;给定一个训练集 D(x1,y1),(x2,y2),...,(xn,yn)D{(x_1,y_1)…

研报精选230308

目录 【行业230308东吴证券】电力设备行业深度报告&#xff1a;氢能深度&#xff1a;绿氢&#xff0c;第四次能源革命的载体【个股230308安信证券_沪光股份】优秀的民营汽车线束供应商&#xff0c;行业国产化替代空间广阔【行业230308中原证券】食品饮料行业2月月报&#xff1a…

银行数字化转型导师坚鹏:数字化转型为什么需要致良知与知行合一

在银行数字化转型过程中&#xff0c;特别需要致良知与知行合一哲学思想的指导。 知中有行&#xff0c;行中有知&#xff1b;行极而知&#xff0c;知极而行&#xff1b;知行无端&#xff0c;知行无始。知与行是一件事&#xff0c;做事与培养本体&#xff08;修心&#xff09;也是…

手动集成Tencent SDK遇到的坑!!!

手动集成的原因 由于腾讯未把Tencent SDK上传到Github中&#xff0c;所以我们不能通过Cocoapods的方式集成&#xff0c;只能通过官方下载其SDK手动集成。 Tencent SDK手动集成步骤 1.访问腾讯开放平台SDK下载界面&#xff0c;找到并下载iOS_SDK_V3.5.1。&#xff08;目前最新…

数组(四)-- LC[167] 两数之和-有序数组

1 两数之和 1.1 题目描述 题目链接&#xff1a;https://leetcode.cn/problems/two-sum/description/ 1.2 求解思路 1. 暴力枚举 最容易想到的方法是枚举数组中的每一个数 x&#xff0c;寻找数组中是否存在 target - x 参考代码 class Solution(object):def twoSum(self, n…

Nginx学习(3)—— 反向代理、负载均衡、动静分离、URLRewrite、防盗链

文章目录网关、代理与反向代理Nginx的反向代理配置基于反向代理的负载均衡的配置负载均衡策略负载均衡权重相关配置&#xff08;weight&#xff09;动静分离Nginx配置动静分离URL-rewrite伪静态配置负载均衡URL-rewrite防盗链网关、代理与反向代理 什么是反向代理&#xff1a;…