目录

  • 是什么
  • 位置
  • 目的
  • 数据结构
  • 什么时候进入字符串常量池
  • 有关JVM参数


是什么

什么是字符串常量池

首先在Java中,字符串是不变量,对字符串的修改其实只是改变了指向(引用)。

String str = "a";//str指向字符串常量池中的"a"
str = "b";//str指向字符串常量池中的"b", 并不是"a"->"b","a”仍然存放在常量池中。

字符串常量池存储字符串,且唯一存储,即相同的字符串只会存储一个。

位置

在Java内存布局中,字符串常量池的存放位置在哪里?

在jdk6及之前,字符串常量池在方法区(永久代)中;jdk7开始,字符串常量池放在堆中。

为什么从方法区移到了堆中?

1,方法区比较小,放在堆中减少OOM
2,方法区的GC频率比较低,堆中GC频率较高,可以有效减少字符串常量池的中字符串。

目的

为什么引入字符串常量池?

由于字符串在java中使用极其频繁,使用常量池可以大大的减少内存消耗。

数据结构

字符串常量池其实是一个HashTable(数组+链表),采用equal方法判断是否存在某个字符串。

什么时候进入字符串常量池

1,字面量字符串,如"abc"
2,string.intern()//如果string中的内容字符串常量池中存在,则返回其引用,不存在:jdk6,在常量池中创建,并返回;jdk7开始,将在字符串常量池中创建一个引用指向string,并返回(原因:jdk7开始,字符串常量池放在了堆中,如果在字符串常量池中在创建相同字面量的对象的话,那么string有两个(一个堆中+一个字符串常量池中)那么浪费了空间)。

举例

String str = "a";//指向字符串常量池中的"a",如不存在,则创建
String str2 = "b";//指向字符串常量池中的"b",如不存在,则创建
String str3 = "ab";//指向字符串常量池中的"ab",如不存在,则创建
String str4 = str+str2;//只要有变量,则会new对象"ab"到堆中,注意这里和之后说的堆都是不包括字符串常量池的,因此str4的"ab",并不是str3的"ab"
str3 == str4;//false
String str5 = str4.intern();//由于"ab"在常量池中已经存在,返回其引用
str4 == str5;//true;
String str6 = new String("abcd");//这里出现了字面量"abcd";那么如果字符串常量池中不存在"abcd",则会创建,由于使用了new关键字,所以会在堆中创建“abcd”,即创建了两个对象
String str7 = str6.intern();//在jdk6中,由于字符串常量池没有"abcd",那么会创建一个,在jdk7开始,会创建一个引用指向str6,不会创建String对象
str6 == str7;//jdk6:false, jdk7开始:true;
String str8 = "a"+"b"+"c"+"d"+"f";//编译器会优化,等同于"abcdf",在字符串常量池中创建了一个对象"abcdf"。
String str9 = "a"+str8;//这种编译器不会优化,因为编译器不能确定str8的值
final String str10 = "a";
final String str11 = "b";
String str12 = str10 + str11;//编译器会优化,因为str10和str11都由final修饰,是不变的。

总结:(往常量池放的前提都是常量池中没有该字符串)
遇到字面量,会往常量池里放。
如果拼接(+操作)有变量,那么会new对象。
直接new对象,就会new对象,位置放在堆中央。
intern(),往常量池中放,并返回其引用(jdk6常量池中新建一个String,jdk7开始常量池中创建一个引用指向当前对象)

有关JVM参数

-XX:StringTableSize = 2000,设置字符串常量池的大小为2000