String、StringBuffer、StringBuilder

String、StringBuffer、StringBuilder

String

final class

    • 所有属性也都是 final 的
    • 原生的保证了基础线程安全
      • 因为无法对它内部数据进行任何修改

典型的 Immutable 类

    • 所以拼接、裁剪字符串等动作,都会产生新的 String 对象
      • 对应用性能有明显影响
      • 操作不当可能会产生大量临时字符串

StringBuffer

可修改字符序列

    • StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组
    • 继承了 AbstractStringBuilder

把各种修改数据的方法都加上 synchronized 关键字

    • 线程安全
      • 也随之带来了额外的性能开销
    • 简单粗暴

StringBuilder

可修改字符序列

    • 在能力上和 StringBuffer 基本相同
    • 继承了 AbstractStringBuilder

Java 1.5 中新增

    • 是绝大部分情况下进行字符串拼接的首选

线程不安全

    • 有效减小了开销

内部数组

内部数组的大小应该如何决定呢?

目前的实现

    • 构建时初始字符串长度加 16
      • 如果构建对象时没有输入最初的字符串
        • 初始值就是 16
      • 可以在创建时,主动指定合适的大小
    • 扩容会产生多重开销
      • 要抛弃原有数组,创建新的数组,还要进行 arraycopy

String的演化

在历史版本中,使用 char 数组保存数据

    • 非常直接
    • Java 中的 char 是两个 bytes 大小
      • 但拉丁语系语言的字符不需要太宽的 char
      • 这样无区别的实现就产生了一定的浪费

Compact Strings

    • 将数据存储方式从 char 数组改变为一个 byte 数组加上一个标识编码的所谓 coder,并且将相关字符串操作类都进行了修改
    • 紧凑字符串
      • 更小的内存占用、更快的操作速度
    • 所有相关的 Intrinsic 之类也都进行了重写
      • 保证没有任何性能损失。
    • 这个特性对于绝大部分应用来说是透明的
      • 因为虽然底层实现发生了改变,但 Java 字符串的行为并没有大的变化,
      • 绝大部分情况不需要修改已有代码

字符串缓存

intern 机制

Intern 是一种显式地排重机制

    • Java的intern机制是指在运行时,如果一个字符串常量(即用双引号括起来的字符串字面量)已经存在于字符串池中,那么在创建新的该字符串常量时,会返回已存在的字符串常量的引用,而不是新创建一个对象。

intern() 方法

    • 作用是提示 JVM 把相应字符串缓存起来,以备重复使用。
    • String 在 Java 6 以后提供
    • 创建字符串对象并调用 intern() 方法时
      • 如果字符串常量池中已经有缓存的字符串
        • 返回缓存里的实例
      • 如果没有
        • 将此String对象添加到池中缓存起来

早期的版本保存在 PermGen中

    • PermGen也就是"永久代"
    • 这个空间是很有限的
    • 基本不会被 FullGC 之外的垃圾收集照顾到
    • 所以,如果使用不当,OOM 就会光顾。

后续版本中保存在堆中

    • 避免永久代占满的问题
      • 永久代在 JDK 8 中被 MetaSpace(元数据区)替代
    • 默认缓存大小也在不断地扩大中
      • -XX:+PrintStringTableStatistics
        • 查看默认缓存
      • -XX:StringTableSize=N
        • 调整默认缓存

G1 GC 下的字符串排重

在 Oracle JDK 8u20 之后,推出了这个新的特性

将相同数据的字符串指向同一份数据

    • JVM 底层的改变,因此不需要修改 Java 类库

目前是默认关闭的

    • -XX:+UseStringDeduplication
      • 开启G1 GC 下的字符串排重功能

Intrinsic 机制

JVM 的底层优化机制

运行的往往就是特殊优化的本地代码,而不是 Java 代码生成的字节码

一种利用 native 方式 hard-coded 的逻辑

-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

    • 查看 intrinsic 发生的状态
相关推荐
虫小宝3 分钟前
Java中的服务化架构设计与实现
java·开发语言
请叫我青哥4 分钟前
第二十条:与抽象类相比,优先选择接口
java·开发语言
ytgytg285 分钟前
SpringBoot返回应答为String类型时,默认带双引号(““),取消双引号的方法
java·spring boot·后端
爱编程的Tom39 分钟前
Map && Set(Java篇详解)
java·开发语言·数据结构·学习·算法
u0104058361 小时前
如何使用Maven管理Java项目依赖
java·开发语言·maven
missterzy1 小时前
Spring Boot 创建定时任务
java·数据库·spring boot·定时任务
德乐懿1 小时前
Spring Boot 2到3升级体验:解锁新特性与优势
java·spring boot·后端
菠菜很好吃1 小时前
Java增加线程后kafka仍然消费很慢
java·开发语言·kafka
空青7261 小时前
AOP与IOC详解
java·服务器·分布式·后端·中间件·面试·架构
拾光师1 小时前
玩转springboot之springboot注册servlet
java