StringTable常量池

本文详细介绍了Java中StringTable的变化,从JDK9开始,String对象的存储结构由char[]变为byte[]加编码标记,以节省内存。文章探讨了String的不可变性、内存分配、基本操作、字符串拼接的性能比较,以及intern()方法的使用。此外,还讨论了G1垃圾收集器中的String去重操作,如何在运行时开启去重并查看统计信息,以减少内存浪费。
摘要由CSDN通过智能技术生成

String介绍

  • String:字符串,使用一对""引起来表示
  • String声明为final的,不可被继承
  • String实现了Serializable接口:表示字符串是支持序列化的。
  • String实现了Comparable接口:表示string可以比较大小
  • String在jdk8及以前内部定义了final char[] value用于存储字符串数据。JDK9时改为byte[]

参考文档:https://blog.csdn.net/unique_perfect/article/details/105137590

 String在jdk9中存储结构变更

官网地址:JEP 254: Compact Strings (java.net)

动机

目前String类的实现将字符存储在一个char数组中,每个字符使用两个字节(16位)。从许多不同的应用中收集到的数据表明,字符串是堆使用的主要组成部分,此外,大多数字符串对象只包含Latin-1字符。这些字符只需要一个字节的存储空间,因此这些字符串对象的内部字符数组中有一半的空间没有被使用。

说明

我们建议将String类的内部表示方法从UTF-16字符数组改为字节数组加编码标志域。新的String类将根据字符串的内容,以ISO-8859-1/Latin-1(每个字符一个字节)或UTF-16(每个字符两个字节)的方式存储字符编码。编码标志将表明使用的是哪种编码。

与字符串相关的类

如AbstractStringBuilder、StringBuilder和StringBuffer将被更新以使用相同的表示方法,HotSpot VM的内在字符串操作也是如此。

这纯粹是一个实现上的变化,对现有的公共接口没有变化。目前没有计划增加任何新的公共API或其他接口。

迄今为止所做的原型设计工作证实了内存占用的预期减少,GC活动的大幅减少,以及在某些角落情况下的轻微性能倒退。

结论:String再也不用char[] 来存储了,改成了byte [] 加上编码标记,节约了一些空间

 String的基本特性

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

  • 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
  • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
  • 当调用string的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

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

String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009。如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降。

使用-XX:StringTablesize可设置StringTable的长度

  • 在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTablesize设置没有要求

  • 在jdk7中,StringTable的长度默认值是60013,StringTablesize设置没有要求

  • 在JDK8中,设置StringTable长度的话,1009是可以设置的最小值

String的内存分配

在Java语言中有8种基本数据类型和一种比较特殊的类型String。这些类型为了使它们在运行过程中速度更快、更节省内存,都提供了一种常量池的概念。

常量池就类似一个Java系统级别提供的缓存。8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种。

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。

  • 如果不是用双引号声明的String对象,可以使用String提供的intern()方法。这个后面重点谈

Java 6及以前,字符串常量池存放在永久代

Java 7中 Oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内

  • 所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样可以让你在进行调优应用时仅需要调整堆大小就可以了。

  • 字符串常量池概念原本使用得比较多,但是这个改动使得我们有足够的理由让我们重新考虑在Java 7中使用String.intern()

Java8元空间,字符串常量池在堆中

StringTable为什么要调整?

官网地址:Java SE 7 Features and Enhancements (oracle.com)

简介:在JDK 7中,内部字符串不再分配在Java堆的永久代中,而是分配在Java堆的主要部分(称为年轻代和老年代),与应用程序创建的其他对象一起。这种变化将导致更多的数据驻留在主Java堆中,而更少的数据在永久代中,因此可能需要调整堆的大小。大多数应用程序将看到由于这一变化而导致的堆使用的相对较小的差异,但加载许多类或大量使用String.intern()方法的大型应用程序将看到更明显的差异。

String的基本操作

class Memory {
    public static void main(String[] args) {//line 1
        int i = 1;//line 2
        Object obj = new Object();//line 3
        Memory mem = new Memory();//line 4
        mem.foo(obj);//line 5
    }//line 9

    private void foo(Object param) {//line 6
   
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

越来越没意思

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值