关于Java里面的字符串常量池的介绍和优化

前言

上一篇文章提到我们在java里面不是通过new创建的string字符串会被放到一个叫字符串常量池的地方,那么本篇文章我们就来详细的了解下常量池的相关知识。

关于String的intern方法

java string pool是jvm里面针对字面量字符串存储的一个特定内存区域。

正是由于string类型的不可变特点,jvm才可以对字面量字字符串做针对的存储优化,在string类里面有一个方法叫intern,当我们创建一个字符串变量并给其赋值的适合,jvm首先会搜索string pool厘米是否存在该value,如果存在java编译器将直接返回其内存的引用地址而不会再给其分配内存,如果不存在则会把这个值添加到pool中并返回引用。

你可以用很简单的一段代码来验证:

String constantString1 = "Baeldung";
        String constantString2 = "Baeldung";

        System.out.println(constantString1==constantString2);//true

基于上面这种形式的创建,我们叫字面量字符串,如果是通过new创建的字符串,则是不一样的,因为它们的存储不在string pool里面,而在jvm的堆里面,每个new的字符串都有自己的内存地址:

String constantString1 = "Baeldung";
        String constantString2 = new String("Baeldung");

        System.out.println(constantString1==constantString2);//false

这个时候我们手动调用intern方法,将其引用放在string pool里面,注意intern方法会判断这个字符串对象的引用是否在常量池中,如果在直接返回引用,如果不在则拷贝对象到常量池中然后在返回引用。

String constantString1 = "Baeldung";
        String constantString2 = new String("Baeldung");

        System.out.println(constantString1==constantString2);//false


        String internString=constantString2.intern();

        System.out.println(constantString1==internString);//true

        System.out.println(constantString2==internString);//false

关于常量池的GC

在jdk7之前,string pool其实是放在PermGen区,这导致其有一个致命的缺点,首先这个区的大小是固定的在运行时不能改变,其次是这个区不会被gc,所以如果我们inter了太多的字符串到string pool中,这个区的内存很容易就满了并且没有回收策略直接导致内存泄漏,故经常会导致OOM的error,直接使jvm崩溃掉。

在jdk7开始及之后这个区被移到了jvm的堆内存里面,最大的好处就是减少了OOM的风险,并且没有被引用的字符串会被gc掉,从而避免了内存泄漏的风险。

性能和优化

在jdk6里面,唯一能做的优化就是调大permgen的空间:

-XX:MaxPermSize=1G

在jdk7,我们可以使用下面的命令来查看string pool的大小:

-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics

` 然后使用如下命令增加其大小:

-XX:StringTableSize=4901

` 增大常量池的大小将会消耗更多的堆内存,但是会提高intern方法的性能,也就是插入到table的性能,因为常量池底层其实也是类似用HashMap的结构来存储数据,所以需要根据具体的场景合理的设置这个值。

JAVA9中的字符串优化

直到java8,string类型底层还是使用char数组进行存储,编码使用utf-16,以便于每个字符可以在内存里面使用2个byte,在java9里面提出来compact string的理念,并在一些情况下可以采用byte数组直接存储字符串,从而节约内存的使用,具体什么时候用char数组什么时候用byte数组,依赖于你存储的内容。

总结

本文介绍了java里面有关字符串常量池的功能,内存分配,优化及注意事项,了解这些之后将更有助于我们对它的使用和掌握。

原文发布于微信公众号 - 我是攻城师(woshigcs)

原文发表时间:2018-05-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JAVA技术站

shell学习五常用命令echo printf test

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

963
来自专栏swag code

多线程的实现方法

      从Thread类中实例化的对象即代表线程,启动一个线程就是建立一个Thread实例。因为完成线程真正功能的代码放在类的run()方法中,所以可以将线...

622
来自专栏开发技术

排序之简单选择排序

  本篇博客是在伍迷兄的博客基础上进行的,其博客地址点击就可以进去,里面好博客很多,我的排序算法都来自于此;一些数据结构方面的概念我就不多阐述了,伍迷兄的博客中...

1112
来自专栏积累沉淀

Java类加载原理机制

1.类的加载过程 JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)链接又分为三个步骤,如下图所示: ? 1...

27010
来自专栏炉边夜话

GCC内嵌汇编语言

绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多...

5872
来自专栏我是业余自学C/C++的

二维变长数组

1495
来自专栏黑泽君的专栏

java基础学习_反射、装饰模式、JDK新特性_day27总结

982
来自专栏Crossin的编程教室

​Python 3 新特性:类型注解

我们知道 Python 是一种动态语言,变量以及函数的参数是不区分类型。因此我们定义函数只需要这样写就可以了:

2812
来自专栏运维技术迷

Python时间与时间戳转换

python的datetime转换为UNIX时间戳 #导入time模块(用于转换时间戳) In [1]: import time   # 导入datetime模...

3855
来自专栏西枫里博客

Python学习笔记九(变量作用域及内置函数和闭包函数)

在上次的学习中,初步认识了Python的自定义函数方式及变量参数。那么编程中的局部变量和全局变量应该是大多数语言的标配。Python中如果定义局部变量和全局变量...

832

扫码关注云+社区

领取腾讯云代金券