前言
上一篇文章提到我们在java里面不是通过new创建的string字符串会被放到一个叫字符串常量池的地方,那么本篇文章我们就来详细的了解下常量池的相关知识。
关于String的intern方法
java string pool是jvm里面针对字面量字符串存储的一个特定内存区域。
正是由于string类型的不可变特点,jvm才可以对字面量字字符串做针对的存储优化,在string类里面有一个方法叫intern,当我们创建一个字符串变量并给其赋值的适合,jvm首先会搜索string pool厘米是否存在该value,如果存在java编译器将直接返回其内存的引用地址而不会再给其分配内存,如果不存在则会把这个值添加到pool中并返回引用。
你可以用很简单的一段代码来验证:
基于上面这种形式的创建,我们叫字面量字符串,如果是通过new创建的字符串,则是不一样的,因为它们的存储不在string pool里面,而在jvm的堆里面,每个new的字符串都有自己的内存地址:
这个时候我们手动调用intern方法,将其引用放在string pool里面,注意intern方法会判断这个字符串对象的引用是否在常量池中,如果在直接返回引用,如果不在则拷贝对象到常量池中然后在返回引用。
关于常量池的GC
在jdk7之前,string pool其实是放在PermGen区,这导致其有一个致命的缺点,首先这个区的大小是固定的在运行时不能改变,其次是这个区不会被gc,所以如果我们inter了太多的字符串到string pool中,这个区的内存很容易就满了并且没有回收策略直接导致内存泄漏,故经常会导致OOM的error,直接使jvm崩溃掉。
在jdk7开始及之后这个区被移到了jvm的堆内存里面,最大的好处就是减少了OOM的风险,并且没有被引用的字符串会被gc掉,从而避免了内存泄漏的风险。
性能和优化
在jdk6里面,唯一能做的优化就是调大permgen的空间:
在jdk7,我们可以使用下面的命令来查看string pool的大小:
`然后使用如下命令增加其大小:
`增大常量池的大小将会消耗更多的堆内存,但是会提高intern方法的性能,也就是插入到table的性能,因为常量池底层其实也是类似用HashMap的结构来存储数据,所以需要根据具体的场景合理的设置这个值。
JAVA9中的字符串优化
直到java8,string类型底层还是使用char数组进行存储,编码使用utf-16,以便于每个字符可以在内存里面使用2个byte,在java9里面提出来compact string的理念,并在一些情况下可以采用byte数组直接存储字符串,从而节约内存的使用,具体什么时候用char数组什么时候用byte数组,依赖于你存储的内容。
总结
本文介绍了java里面有关字符串常量池的功能,内存分配,优化及注意事项,了解这些之后将更有助于我们对它的使用和掌握。
领取专属 10元无门槛券
私享最新 技术干货