JVM(十四)—— StringTable

news2024/11/28 7:31:39

JVM(十四)—— StringTable

  • String的基本特性
  • String的内存分配
  • 字符串拼接
  • intern方法
    • 常见面试题:到底创建了几个String对象

String的基本特性

作为一名Java程序员肯定少不了和 String打交道,使用方法就是将字符串用一对""引起来表示。查看String的源码,使用final修饰,说明是不可被继承的。String实现了java.io.Serializable接口,表示祖父穿是支持序列化的,实现了Comparable接口,表示String可以比较大小。

String 在JDK8及之前内部定义了final char value[]用于存储字符串数据,在JDK9时改成了byte[].
在这里插入图片描述
我们知道一个char数组中的char占用两个字节,大部分的String包含的都是拉丁字符,用一个字节就可以存储了,导致了使用char数组空间的浪费,在JDK9进行了修改,变成了byte[]。但是对于中文存储需要两个字符,所以还加了一个encoding-flag字符编码进行标识。

当然基于String的类比如StringBuffer,StringBuilder也做了修改,包括HotSpot虚拟机内部固有的String结构。

String代表不可变的字符序列,简称不可变性。

  • 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
  • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  • 当调用String的replace方法修改指定字符或者字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
    通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

字符串常量池中是不会存储相同内容的字符串的。

  • String的String Pool是一个固定大小的Hashtable,JDK6的大小长度固定为1009,JDK7中长度默认是60013,JDK8开始1009是可设置的最小值。
  • 如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接回造成的影响就是当调用String.intern时的性能会大幅下降。
  • 使用-XX:StringTableSize可设置StringTable的长度。

String的内存分配

Java中的8种基本数据类型和String为了使它们在运行过程中速度更快,更节省内存,提供了一种常量池的概念。
常量池就类似于一个Java系统界别提供的缓存。8种基本数据类型的常量池都是系统协调的。String类型的常量池比较特殊,主要的使用方法有两种:直接使用双引号生命出来的String对象会直接存储在常量池中,或者使用String提供的intern方法

  • JDK6及以前,字符串常量池存放在永久代。
    *在这里插入图片描述
  • JDK7中将字符串常量池的位置调整到Java堆中。

在这里插入图片描述
* 所有的字符串都保存在堆中。和其他普通对象一样,这样可以在进行调优应用是仅需要调整堆大小就可以了。

  • JDK8元空间,字符串常量在堆。

在之前的方法区篇章说过StringTable调整的原因:永久代空间太小,永久代的垃圾回收频率较低,会经常出现OOM异常

字符串拼接

在我们的的开发中经常用到字符串的拼接,那字符串执行拼接的原理又是什么呢?

  • 常量与常量的拼接结果在常量池,原理是编译期优化。
public static void main(String[] args) {
        String s1 = "a" + "b" + "c";  // 在编译期优化为abc
        String s2 = "abc";
        System.out.println(s1 == s2); // true

    }

上边的代码会返回true, String s1 = “a” + “b” + “c”;在编译期优化为abc,查看class文件也可以看出来。
在这里插入图片描述

  • 只要其中有一个是变量,结果就在堆中,变量拼接的原理是StringBulider.
  • 如果拼接的结果调用intern方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
public static void main(String[] args) {
        String s1 = "java";
        String s2 = "hadoop";

        String s3 = "javahadoop";
        // 如果拼接符号的前后出现了变量。则相当于在堆空间中new String(), 具体的额内容为拼接的结果:javahadoop
        String s4 = s1 + "hadoop";
        String s5 = "java" + s2;
        String s6 = s1 + s2;



        System.out.println(s3 == s4); // false
        System.out.println(s3 == s5); // false
        System.out.println(s3 == s5); // false
        System.out.println(s3 == s6); // false
        System.out.println(s4 == s5); // false
        System.out.println(s4 == s6); // false
        System.out.println(s5 == s6); // false

        // intern() 判断字符串常量池中是否存在javahadoop值,
        // 如果存在,则返回常量池中javahadoop的地址,如果不存在,则在常量池中加载一份javahadoop,并返回此对象的地址。
        String s7 = s5.intern();
        System.out.println(s3 == s7); // true


    }

通过查看字节码:
在这里插入图片描述
我们发现在进行拼接符号的前后出现了变量的操作时,是用的是StringBuilder的append方法,当拼接完之后,调用toString方法转成String对象。

但是有变量的拼接一定是使用StringBuilder吗?我们来看下边的例子:

 public static void main(String[] args) {
        final String s1 = "java";
        final String s2 = "hadoop";

        // 如果拼接符号左右两边都是字符串常量或常量引用,则仍然时候编译期优化。
        String s3 = "javahadoop";
        String s6 = s1 + s2;

        System.out.println(s3 == s6); // true
        
    }

查看class文件:

在这里插入图片描述
从class文件可以看出:如果拼接符号左右两边都是字符串常量或常量引用,则仍然时候编译期优化。

根据上边的分析:使用字符串+字符串拼接的方式,会大量的创建StringBuilder和String对象,不仅仅时间成本高,还会占用很多内存,如果进行GC,还会花费额外的时间。如果我们使用StringBuilder的append方法自始至终只创建一个对象,时间更短,占用内存更小。所以在实际的开发中对于字符串的拼接建议使用StringBuilder的append方法操作。

如果基本确定要添加的字符创的长度的最大值,我们可以使用new StringBuilder(int capacity)创建,这样也会减少扩容的时间。

intern方法

如果不是用双引号声明的String对象,可以使用String提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。

也就是说如果在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。

通俗点讲,interned String就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部常量池。

常见面试题:到底创建了几个String对象

new String("ab");

上边的代码创建了几个String对象?
查看字节码:
在这里插入图片描述
从上边的字节码可以看到一共创建了两个对象:
第一个:new关键字在堆空间创建的。
第二个:字符串常量池中的对象。

String str = new String("a") + new String("b");

上边的代码创建了几个String对象
查看字节码:
在这里插入图片描述
从字节可以看到创建了以下对象:
① new StringBuilder
② new String(“a”)
③ 常量池中的"a"
④ new String(“b”)
⑤ 常量池中的"b"
深入剖析:StringBuilder.toString()会创建new String对象.
⑥ new String(“ab”) ,toString方法的调用,在字符创常量池中没有生成"ab"。

接下来我们从内存的角度分析一下代码:

 public static void main(String[] args) {
        String str = new String("1");
        str.intern(); // 执行之前字符串常量池中已经有1了
        String s2 = "1";
        System.out.println(str == s2); // false
    }

从上边的介绍可以知道new String(“1”)创建了两个对象,str指向的是堆空间创建的对象地址,而str.intern()执行之前字符串常量池中已经有1了,所以s2指向的是字符串常量池中的对象.所以str == s2为false。

我们在看一个例子:

 public static void main(String[] args) {
        String str = new String("1") +  new String("1"); // str记录的地址为new String("11")
        // 执行完上一行代码以后,字符串场景莲池中, 不存在"11"
        str.intern(); // jdk6中常量池存放的是"11"的对象地址。jdk7此时常量池中没有创建"11",为了节省空间,在常量池存储的是上边创建的new String("11")对象的地址
        String s2 = "11";//在jdk6中存的是"11"常量池的地址,jdk7存的是引用地址
        System.out.println(str == s2); // true
    }

总结String的intern的使用:

  • JDK6中,将字符串对象尝试放入常量池
    • 如果串池中有,则不会放入,返回已有的串池中的对象的地址。
    • 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址。
  • JDK7开始,将字符串对象尝试放入常量池
    • 如果串池中有,则不会放入,返回已有的串池中的对象的地址。
    • 如果没有,会把此对象引用地址复制一份,放入串池,并返回串池中的引用地址。

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

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

相关文章

SpringCloud 远程调用

目录 1. SpringCloud 2. Nacos 3. 远程通信 3.1 创建公共子模块 (nacos_commons) 3.1.1 DTO对象 3.2 父项目引入子项目 (nacos_commons) 打成的jar包 3.3 父项目指向子项目 (nacos_commons) 为儿子 3.4 子项目 (nacos_provider) 3.5 子项目 (nacos_consumer) …

【Netty】九、Netty自定义协议

Netty自定义协议一、Netty自定义协议二、 协议设计三、 协议实现编码:解码:时间轮算法Netty中的时间轮一、Netty自定义协议 公有协议(http、tcp)、私有协议(自己定义的,不是行业标准) 我们知道…

[Qt]QMainWindow

目录 1.基本概述 2.菜单栏 3.工具栏 4.状态栏 5.铆接部件 6.中心部件 7.资源文件 (1)创建菜单栏,及菜单项 (2)创建工具栏 (3)创建锚接部件 (4)创建中心文件 (5)创建状态栏 1.基本概述 QMainWindow是一个为…

腾讯网关TGW基础原理入门

本文是在组内技术分享的发言稿,主要介绍 TGW 基本原理和架构,同时为了加深理解,会辅助对比 TGW 与 LVS(ipvs)的异同。 本次分享是偏基础性的 TGW 介绍,不会特别深入技术细节,目的是帮助需要用到…

算法7:迪杰斯特拉算法

目录1. 应用场景-最短路径问题2. 迪杰斯特拉(Dijkstra)算法介绍3. 迪杰斯特拉(Dijkstra)算法过程4. 算法分析过程5. 代码实现1. 应用场景-最短路径问题 看一个应用场景和问题 胜利乡有7个村庄(A, B, C, D, E, F, G) ,现在有六个邮差,从G点出发&#xff…

贪吃蛇OneDay

环境 配置git环境 安装Git Bash(使用Mac和Linux的同学可以跳过这一步):https://gitforwindows.org/ 进入家目录生成秘钥:执行命令ssh-keygen 在Ac Git上注册账号,地址:https://git.acwing.com/ 将id_rsa.p…

Unity中的AssetBundle

AssetBundle的概念 AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包,是对Unity 初始Resources的一种扩展;一般使用的策略是把必须的资源和不需要更新的资源放在Resources文件夹下,其他的资源放在AssetBundle下面…

【微信小程序】flex布局

🏆今日学习目标:flex布局 😃创作者:颜颜yan_ ✨个人主页:颜颜yan_的个人主页 ⏰预计时间:20分钟 🎉专栏系列:微信小程序开发 文章目录前言Flex布局什么是flex?flex-direc…

Hive中内部表、外部表、分区表、分桶表之间的关系

文章目录Hive中内部表、外部表、分区表、分桶表之间的关系1.0🚀内部表2.0👀外部表3.0🫥内部表和外部表的差异3.0🐘分区表4.0😃分桶表Hive中内部表、外部表、分区表、分桶表之间的关系 1.0🚀内部表 内部表&…

HEAD: HEtero-Assists Distillation for Heterogeneous Object Detectors

ECCV 2022 异质辅助蒸馏 Abstract Conventional knowledge distillation (KD) methods for object detection mainly concentrate on homogeneous teacher-student detectors. However, the design of a lightweight detector for deployment is often significantly differ…

计算机毕业设计(附源码)python智能仓储进出货管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

web前端期末大作业:基于HTML+CSS+JavaScript制作我的音乐网站(带设计报告)

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

【python】都2022年不会还有人不会在电脑桌面上养宠物吧~

前言 嗨喽~大家好呀,这里是魔王呐 ! 上班枯燥,对着冷冰冰的电脑,相信很多小伙伴即使摸鱼,心情也不愉快。 这时如果有个萌宠能大家进行实时互动,这该有多好呀。再无聊的工作也能增添那么一丝趣味。 今天博主就来给大…

2、Ubuntu下安装Vivado下的下载器驱动 Digilent 版本

简介 在Ubuntu下安装Vivado时,安装工具会提醒你,digilent驱动无法自动安装,需要手动安装,并且让用户参考UG973手册安装。 由于安装驱动很简单,不用麻烦大家去找手册了,这里直接给出安装方法 安装方法 …

【Pytorch Lighting】第 6 章:深度生成模型

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

FPGA HLS 卷积单元 数据类型hls优化约束设置

数据类型 自定义精度整形&#xff1a; ap_int<4> in1, in2; ap_int<8> concat; concat (in1, in2); // in1和in2拼起来&#xff08;按照补码拼起来&#xff09; /* 例子&#xff1a; in1 1, in2 -1 补码&#xff1a; in1 0001 in2 1001 > 11101 > 1…

Spring mvc处理异常

文章目录一、Handler ExceptionResolver处理异常二、ExceptionHandler注解三、重点&#xff1a;添加ExceptionHandler注解方法的形参只能是异常类型四、重点2&#xff1a;捕获所有方法的异常—ControllerAdvice注解五、总结六、ResponseStatusExceptionResolve自定义异常显示页…

[go学习笔记.第十一章.项目案例] 1.家庭收支记账软件项目

一.基本介绍 1.项目开发流程说明 2.项目需求说明 目标: 模拟实现一个基于文本界面的<<家庭记账软件>> 掌握初步的编程技巧和调试技巧 主要涉及以下知识点 : (1).局部变量和基本数据类型 (2).循环语句 (3).分支语句 (4).简单的屏幕输出格式控制 (5).进阶&#xff1…

刷题日记【第九篇】-笔试必刷题【杨辉三角的变形+计算某字符出现的次数+字符串通配符+统计每个月兔子的总数】

下列sql语句中哪条语句可为用户zhangsan分配数据库userdb表userinfo的查询和插入数据权限&#xff08;A&#xff09;。 常用的管理权限的命令为&#xff1a; grant select/insert/update/delete on 数据库名.表名 to 用户名‘该用户允许访问的ip’ 在oracle中&#xff0c;下面哪…

世界上只有一种共识算法,那就是Paxos

分布式系统原理系列目录 分布式系统的麻烦副本与一致性为什么需要一个分布式共识算法世界上只有一种共识算法&#xff0c;那就是PaxosCAP定理&#xff0c;说起来一句话&#xff0c;实际坑不少BASE&#xff0c;可用性高于强一致性分布式事务方案那么多&#xff0c;到底该选哪一…