JVM字符串常量池与String中intern方法
这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
之前讲解了JVM的一系列垃圾回收算法和堆栈的一些结构以及垃圾回收器,突然想起还有字符串常量池没有讲解,今天准备讲解下字符串常量池是个什么样的东西.
设计背景
首先字符串跟普通对象一样,在创建的时候一样需要花费时间与空间,并且字符串是使用最频繁的对象,并且存在重复的字符串被多次创建,那么就会大量频繁的创建字符串,极大程度地影响程序的性能,平时设计中我们会对经常访问的对偶像创建一个缓存来存储,因此JVM设计了字符串常量池.
设计实例
JVM为了提高性能和减少内存的开销,因此在字符串实例化的时候做了一些步骤,来进行优化,步骤如下:
- 为字符串开辟一块区域叫做一个字符串常量池,类似于缓存区
- 创建字符串常量时,首先查询字符串常量池是否存在该字符串(类似判断缓存是否存在)
- 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
根据上面的介绍,个人觉得可以将常量池当缓存来进行理解
字符串常量池位置
-
Jdk1.6及之前: 有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池
-
Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里
-
Jdk1.8及之后: 无永久代,运行时常量池在元空间,字符串常量池里依然在堆里
三种字符串操作
- 直接赋值字符串
String name = "zhangsan";
这种方式创建的字符串对象,name会指向常量池中的引用,因为有"zhangsan"这个字面量,
创建对象name的时候,JVM会先去常量池中通过 equals(key) 方法,
判断是否有相同的对象如果有,则直接返回该对象在常量池中的引用;
如果没有,则会在常量池中创建一个新对象,再返回引用。
- new String()
String name = new String("lisi");
这种方式会保证字符串常量池和堆中都有这个对象,没有就创建,最后返回堆内存中的对象引用。
步骤大致如下:
因为有"lisi"这个字面量,所以会先检查字符串常量池中是否存在字符串"lisi"
不存在,先在字符串常量池里创建一个字符串对象;再去内存中创建一个字符串对象"lisi"
存在的话,就直接去堆内存中创建一个字符串对象"lisi"
最后,将内存中的引用返回。
- intern()
String s1 = new String("wangwu");
String s2 = s1.intern();
//false
System.out.println(s1 == s2);
String中的intern方法是一个native的方法,当调用intern方法时,如果池已经包含一个等于此String对象的字符串
(用equals(oject)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串 s1
总结
字符串常量池在一定程度上来说相当于字符串的一个缓存,在java中除了字符串以外,Byte,Short,Integer,Long,Character这5种整型的包装类也实现了在对应值小于等于127时的一个对象池,当对应值小于127创建的数值相同的对象是同一个对象,因为一般这种比较小的数用到的概率相对较大。
最后感谢大家的阅读!