无名商城论坛

搜索
查看: 322|回复: 0

[其他技术] 【LSP】Java 字符串常量池、字符串比较/拼接问题、Stri

[复制链接]

1万

主题

1万

帖子

3万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
32464
发表于 2022-5-8 17:04:19 | 显示全部楼层 |阅读模式


1.字符串的比较
1. 1 字符串常量池
字符串常量池(以下简称常量池/字符串池)的存在意义:实际开发中,String类是使用频率非常高的一种引用对象类型。但是不断地创建新的字符串对象,会极大地消耗内存。因此,JVM为了提升性能和减少内存开销,内置了一块特殊的内存空间即常量池,以此来避免字符串的重复创建。JDK 1.8 后,常量池被放入到堆空间中。

字符串常量池的特点是有且只有一份相同的字面量,如果有其它相同的字面量,JVM则返回这个字面量的引用地址,如果没有相同的字面量,则在字符串常量池创建这个字面量并返回它的引用地址。

字符串常量池是全局共享的,故也称全局字符串池。字符串池中维护了共享的字符串对象,这些字符串不会被垃圾收集器回收。

1.1.1 字符串常量池在Java内存区域的存放位置?
在 JDK6.0及之前版本,字符串常量池是放在 Perm Gen区 (也就是方法区) 中,方法区与堆分离。
由于方法区的内存空间太小,在 JDK 7.0版本,字符串常量池被移到了堆中。

1.1.2 字符串常量池是如何实现的?
在 HotSpot 虚拟机里实现的 String Pool 功能的是一个 StringTable 类,它是一个 Hash 表,默认值大小长度是1009;这个 StringTable 在每个 HotSpot 虚拟机的实例只有一份,被所有的类共享。字符串常量由一个一个字符组成,放在了 StringTable 上。

在 JDK 6.0中,StringTable 的长度是固定的,长度就是1009,因此如果放入 String Pool 中的String类字符串非常多,就会造成哈希冲突,导致链表过长,当调用 String.intern()时会需要到链表上一个一个查找,从而导致性能大幅度下降。

在 JDK7.0中,StringTable的长度可以通过参数指定:
-XX:StringTableSize=66666

1.2 String 类型的比较方式
若直接使用“==”进行比较对象,则比较的是两个对象的引用地址;

若使用str1.equals(str2)方法进行比较,由于String类内部已经覆盖Object类中的equals()方法,实际比较的是两个字符串的值。

比较原理:
先判断对象地址是否相等,若相等则直接返回true;
若不相等再去参数判断括号内传入的参数是否为String类型的:若不是字符串将最终返回false;若是字符串,再依次比较所有字符是否一样。
// 源码
public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
                return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value);
        }
     }
     return false;
}
1.3 String 的创建方式
1.3.1 直接使用“=”进行字面量赋值
String str_01 = "aa";
String str_02 = "aa";
System.out.println(str_01 == str_02);
.java文件编译后得到 .class文件,里面包含了类的信息,其中有一块叫做常量池(Constant Pool)的区域 (.class常量池和内存中的 String Pool 并不是同一个东西),.class文件常量池主要存储的就包括字面量,字面量包括类中定义的常量,由于String是不可变的(String为什么是不可变的?)所以字符串“Hello”就存放在 .class文件常量池里。

当程序调用类时,.class文件被解析到内存中的方法区,同时 .class文件中的常量池信息会被加载到运行时常量池。但 String类不会这样,“aa”会在堆区中创建一个对象,同时会在 String Pool 存放一个它的引用。

此时类刚刚被加载,main函数中的 str_01 并没有被创建,而“aa”对象已经创建在于堆中。
当主线程开始创建 str_01 变量时,JVM会去 String Pool 中查找是否有 与“aa”相等的String,如果相等就把在字符串池中“Hello”的引用赋值给str。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表