前言

研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池。Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串。它的实现是为了提高字符串操作的性能并节省内存。它也被称为String Intern PoolString Constant Pool。那让我来看看究竟是怎么一回事吧。

欢迎关注微信公众号「JAVA旭阳」交流和学习

理解字符串常量池

当您从在类中写一个字符串字面量时,JVM将首先检查该字符串是否已存在于字符串常量池中,如果存在,JVM 将返回对现有字符串对象的引用,而不是创建新对象。我们通过一个例子更好的来理解。

比如下面的代码:

String s1 = "Harry Potter";
String s2 = "The Lord of the Rings";
String s3 = "Harry Potter";

在这段代码中,JVM 将创建一个值为“Harry Potter”的字符串对象,并将其存储在字符串常量池中。s1和s3都将是对该单个字符串对象的引用。

如果s2的字符串内容“The Lord of the Rings”不存在于池中,则在字符串池中生成一个新的字符串对象。

两种创建字符串方式

Java 编程语言中有两种创建 String 的方法。第一种方式是使用String Literal字符串字面量的方式,另一种方式是使用new关键字。他们创建的字符串对象是都在常量池中吗?

  • 字符串字面量的方式创建
String s1 = "Harry Potter";
String s2 = "The Lord of the Rings";
String s3 = "Harry Potter";
  • new关键字创建
String s4 = new String("Harry Potter");
String s5 = new String("The Lord of the Rings");

我们来比较下他们引用的是否是同一个对象:

s1==s3 //真
s1==s4 //假
s2==s5 //假

使用 == 运算符比较两个对象时,它会比较内存中的地址。

正如您在上面的图片和示例中看到的,每当我们使用new运算符创建字符串时,它都会在 Java 堆中创建一个新的字符串对象,并且不会检查该对象是否在字符串常量池中。

那么我现在有个问题,如果是字符串拼接的情况,又是怎么样的呢?

字符串拼接方式

前面讲清楚了通过直接用字面量的方式,也就是引号的方式和用new关键字创建字符串,他们创建出的字符串对象在堆中存储在不同的地方,那么我们现在来看看用+这个运算符拼接会怎么样。

例子1

public static void test1() {
// 都是常量,前端编译期会进行代码优化
// 通过idea直接看对应的反编译的class文件,会显示 String s1 = "abc"; 说明做了代码优化
String s1 = "a" + "b" + "c";
String s2 = "abc"; // true,有上述可知,s1和s2实际上指向字符串常量池中的同一个值
System.out.println(s1 == s2);
}
  • 常量与常量的拼接结果在常量池,原理是编译期优化。

例子2

public static void test5() {
String s1 = "javaEE";
String s2 = "hadoop"; String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2; System.out.println(s3 == s4); // true 编译期优化
System.out.println(s3 == s5); // false s1是变量,不能编译期优化
System.out.println(s3 == s6); // false s2是变量,不能编译期优化
System.out.println(s3 == s7); // false s1、s2都是变量
System.out.println(s5 == s6); // false s5、s6 不同的对象实例
System.out.println(s5 == s7); // false s5、s7 不同的对象实例
System.out.println(s6 == s7); // false s6、s7 不同的对象实例
}
  • 只要其中有一个是变量,结果就在堆中, 变量拼接的底层原理其实是StringBuilder

例子3:

public void test6(){
String s0 = "beijing";
String s1 = "bei";
String s2 = "jing";
String s3 = s1 + s2;
System.out.println(s0 == s3); // false s3指向对象实例,s0指向字符串常量池中的"beijing"
String s7 = "shanxi";
final String s4 = "shan";
final String s5 = "xi";
String s6 = s4 + s5;
System.out.println(s6 == s7); // true s4和s5是final修饰的,编译期就能确定s6的值了
}
  • 不使用final修饰,即为变量。如s3行的s1和s2,会通过new StringBuilder进行拼接
  • 使用final修饰,即为常量。会在编译器进行代码优化。

妙用String.intern() 方法

前面提到new关键字创建出来的字符串对象以及某些和变量进行拼接不会在字符串常量池中,而是直接在堆中新建了一个对象。这样不大好,做不到复用,节约不了空间。那有什么好办法呢?intern()就派上用场了,这个非常有用。

intern()方法的作用可以理解为主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

String s6 = new String("The Lord of the Rings").intern();

s2==s6 //真
s2==s5 //假

字符串常量池有多大?

关于字符串常量池究竟有多大,我也说不上来,但是讲清楚它底层的数据结构,也许你就明白了。

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

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

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

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

字符串常量池的优缺点

字符串池的优点

  • 提高性能。由于 JVM 可以返回对现有字符串对象的引用而不是创建新对象,因此使用字符串池时字符串操作更快。
  • 共享字符串,节省内存。字符串池允许您在不同的变量和对象之间共享字符串,通过避免创建不必要的字符串对象来帮助节省内存。

字符串池的缺点

  • 它有可能导致性能下降。从池中检索字符串需要搜索池中的所有字符串,这可能比简单地创建一个新的字符串对象要慢。如果程序创建和丢弃大量字符串,则尤其如此,因为每次使用字符串时都需要搜索字符串池。

总结

其实在 Java 7 之前,JVM将 Java String Pool 放置在PermGen空间中,它具有固定大小——它不能在运行时扩展,也不符合垃圾回收的条件。在PermGen(而不是堆)中驻留字符串的风险是,如果我们驻留太多字符串,我们可能会从 JVM 得到一个OutOfMemory错误。从 Java 7 开始,Java String Pool存放在Heap空间,由 JVM进行垃圾回收。这种方法的优点是降低了OutOfMemory错误的风险,因为未引用的字符串将从池中删除,从而释放内存。

现在通过本文的学习,你该知道如何更好的创建字符串对象了吧。

欢迎关注微信公众号「JAVA旭阳」交流和学习

更多学习资料请移步:程序员成神之路

正确理解和使用JAVA中的字符串常量池的更多相关文章

  1. Java中的字符串常量池,栈和堆的概念

    问题:String str = new String(“abc”),“abc”在内存中是怎么分配的?    答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...

  2. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  3. Java中String字符串常量池

    首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...

  4. Java中String字符串常量池总结

    最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...

  5. Java进阶——Java中的字符串常量池

    转载. https://blog.csdn.net/qq_30379689/article/details/80518283 字符串常量池 JVM为了减少字符串对象的重复创建,其内部维护了一个特殊的内 ...

  6. Java中的字符串常量池

    ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  7. Java中的字符串常量池和JVM运行时数据区的相关概念

    什么是字符串常量池 JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池 工作原理 当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量 ...

  8. Java中的字符串常量池?

    参考:http://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/index.html

  9. Java String:字符串常量池(转)

    作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么? 字符串常量池的设计思想是什么? 字符串常量池在哪里? 如何操作字符串常量 ...

  10. Java SE之字符串常量池

    Reference Document: 什么是字符串常量池?   http://www.importnew.com/10756.html[Recommend] Java常量池理解与总结   http: ...

随机推荐

  1. Python实验报告(第四周

    一.实验目的和要求 学会应用列表.元组.字典等序列: 二.实验环境 软件版本:Python 3.10 64_bit 三.实验过程 1.实例1:输出每日一贴 (1)在IDLE中创建一个名称为tips.p ...

  2. BZOJ2654 tree (wqs二分)

    题目描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解.   一个最小生成树问题,但是我们要选need条白边,我们用g(i)表示选取i条 ...

  3. C#实现登录某web进而获取其token数据

    实习在学C#,记录一下学习过程! 首先是需求描述(基于C#的.net core MVC实现): User: Resource Owner Agent:Brower auth.brightspace.c ...

  4. Shading-JDBC、ShadingSphere、ShardingProxy 使用详解

    ShadingSphere ​ShardingSphere是一款起源于当当网内部的应用框架,2015年在当当网内部诞生,2016年由主要开发人员张亮带入京东数科,在国内经历了当当网.电信翼支付.京东数 ...

  5. 35.ListSerializer详解

    ListSerializer继承了BaseSerializer 一般情况下我们不直接使用ListSerializer ListSerializer会自动调用,序列化many=True的字段时,会自动调 ...

  6. VMware Fusion配置NAT静态IP

    前言 本主机 CentOS8.2 Mac VMware Fusion 我们在使用虚拟机的时候,经常遇到这样的问题,我们会换地方,IP 会变化,如果虚拟机使用桥接的方式,那么很多与 IP 相关的服务都会 ...

  7. LVS之NAT、DR、TUNNEL实验

    1.LVS-NAT规则+WRR算法 服务器 IP地址 作用 系统版本 RS1 10.0.0.8/24GW:10.0.0.101 网站服务器 Rocky8.6 RS2 10.0.0.18/24GW:10 ...

  8. 用Nodejs 实现一个简单的 Redis客户端

    目录 0. 写在前面 1. 背景映入 2. 数据库选择 3. Nodejs TCP连接 3. 代码编写 4. 实验 5. wireshark 抓包分析 6. 杂与代码 0. 写在前面 大家如果有去看过 ...

  9. Nginx 使用自签名证书实现 https 反代 Spring Boot 中碰到的页面跳转问题

    问题一:页面自动跳转到 80 端口 问题描述 最近在使用Nginx反代一个Spring Boot项目中碰到了一个问题,使用 Spring Boot 中的 redirect: 进行页面跳转的时候,通过 ...

  10. php注解使用示例

    今天看到php注解的介绍文章很感兴趣,动手实际试了试挺好玩,写这篇文章记录下 php从8开始支持原生注解功能了,我们可以写个小的例子看看注解怎么玩.   先确定我们的任务目标 1.编写一个注解类rou ...