转载:Java中的字符串常量池详细介绍
引用自:http://blog.csdn.net/langhong8/article/details/50938041
这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池,需要的朋友可以参考下
Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");,这两种方式我们在代码编写时都经常使用,尤其是字面量的方式。然而这两种实现其实存在着一些性能和内存占用的差别。这一切都是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池。
工作原理
当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。
举例说明
字面量创建形式
复制代码代码如下:
String str1 = "droid";
JVM检测这个字面量,这里我们认为没有内容为droid的对象存在。JVM通过字符串常量池查找不到内容为droid的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给变量str1。
如果接下来有这样一段代码
复制代码代码如下:
String str2 = "droid";
同样JVM还是要检测这个字面量,JVM通过查找字符串常量池,发现内容为”droid”字符串对象存在,于是将已经存在的字符串对象的引用返回给变量str2。注意这里不会重新创建新的字符串对象。
验证是否为str1和str2是否指向同一对象,我们可以通过这段代码
复制代码代码如下:
System.out.println(str1 == str2);
结果为true。
使用new创建
复制代码代码如下:
String str3 = new String("droid");
当我们使用了new来构造字符串对象的时候,不管字符串常量池中有没有相同内容的对象的引用,新的字符串对象都会创建。因此我们使用下面代码测试一下,
复制代码代码如下:
String str3 = new String("droid");
System.out.println(str1 == str3);
结果如我们所想,为false,表明这两个变量指向的为不同的对象。
intern
对于上面使用new创建的字符串对象,如果想将这个对象的引用加入到字符串常量池,可以使用intern方法。
调用intern后,首先检查字符串常量池中是否有该对象的引用,如果存在,则将这个引用返回给变量,否则将引用加入并返回给变量。
复制代码代码如下:
String str4 = str3.intern();
System.out.println(str4 == str1);
输出的结果为true。
疑难问题
前提条件?
字符串常量池实现的前提条件就是Java中String对象是不可变的,这样可以安全保证多个变量共享同一个对象。如果Java中的String对象可变的话,一个引用操作改变了对象的值,那么其他的变量也会受到影响,显然这样是不合理的。
引用 or 对象
字符串常量池中存放的时引用还是对象,这个问题是最常见的。字符串常量池存放的是对象引用,不是对象。在Java中,对象都创建在堆内存中。
更新验证,收到的很多评论也在讨论这个问题,我简单的进行了验证。 验证环境:
复制代码代码如下:
22:18:54-androidyue~/Videos$ cat /etc/os-release
NAME=Fedora
VERSION="17 (Beefy Miracle)"
ID=fedora
VERSION_ID=17
PRETTY_NAME="Fedora 17 (Beefy Miracle)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:17"
22:19:04-androidyue~/Videos$ java -version
java version "1.7.0_25"
OpenJDK Runtime Environment (fedora-2.3.12.1.fc17-x86_64)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
验证思路:以下的Java程序读取一个大小为82M的视频文件,以字符串形式进行intern操作。
复制代码代码如下:
22:01:17-androidyue~/Videos$ ll -lh | grep why_to_learn.mp4
-rw-rw-r--. 1 androidyue androidyue 82M Oct 20 2013 why_to_learn.mp4
验证代码
复制代码代码如下:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class TestMain {
private static String fileContent;
public static void main(String[] args) {
fileContent = readFileToString(args[0]);
if (null != fileContent) {
fileContent = fileContent.intern();
System.out.println("Not Null");
}
}
private static String readFileToString(String file) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
StringBuffer buff = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
buff.append(line);
}
return buff.toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
由于字符串常量池存在于堆内存中的永久代,适用于Java8之前。我们通过设置永久代一个很小的值来进行验证。如果字符串对象存在字符串常量池中,那么必然抛出java.lang.OutOfMemoryError permgen space错误。
复制代码代码如下:
java -XX:PermSize=6m TestMain ~/Videos/why_to_learn.mp4
运行证明程序没有抛出OOM,其实这个不能很好的证明存储的是对象还是引用。
但是这个至少证明了字符串的实际内容对象char[]不存放在字符串常量池中。既然这样的话,其实字符串常量池存储字符串对象还是字符串对象的引用反而不是那么重要。但个人还是倾向于存储的为引用。
优缺点
字符串常量池的好处就是减少相同内容字符串的创建,节省内存空间。
如果硬要说弊端的话,就是牺牲了CPU计算时间来换空间。CPU计算时间主要用于在字符串常量池中查找是否有内容相同对象的引用。不过其内部实现为HashTable,所以计算成本较低。
GC回收?
因为字符串常量池中持有了共享的字符串对象的引用,这就是说是不是会导致这些对象无法回收?
首先问题中共享的对象一般情况下都比较小。据我查证了解,在早期的版本中确实存在这样的问题,但是随着弱引用的引入,目前这个问题应该没有了。
关于这个问题,可以具体了解这片文章interned Strings : Java Glossary
intern使用?
关于使用intern的前提就是你清楚自己确实需要使用。比如,我们这里有一份上百万的记录,其中记录的某个值多次为美国加利福尼亚州,我们不想创建上百万条这样的字符串对象,我们可以使用intern只在内存中保留一份即可。关于intern更深入的了解请参考深入解析String#intern。
总有例外?
你知道下面的代码,会创建几个字符串对象,在字符串常量池中保存几个引用么?
复制代码代码如下:
String test = "a" + "b" + "c";
答案是只创建了一个对象,在常量池中也只保存一个引用。我们使用javap反编译看一下即可得知。
复制代码代码如下:
17:02 $ javap -c TestInternedPoolGC
Compiled from "TestInternedPoolGC.java"
public class TestInternedPoolGC extends java.lang.Object{
public TestInternedPoolGC();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: ldc #2; //String abc
2: astore_1
3: return
看到了么,实际上在编译期间,已经将这三个字面量合成了一个。这样做实际上是一种优化,避免了创建多余的字符串对象,也没有发生字符串拼接问题。关于字符串拼接,可以查看Java细节:字符串的拼接。
转载:Java中的字符串常量池详细介绍的更多相关文章
- Java中的字符串常量池,栈和堆的概念
问题:String str = new String(“abc”),“abc”在内存中是怎么分配的? 答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...
- Java中String字符串常量池总结
最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...
- Java中String字符串常量池
首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...
- Java中的字符串常量池和JVM运行时数据区的相关概念
什么是字符串常量池 JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池 工作原理 当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量 ...
- Java进阶——Java中的字符串常量池
转载. https://blog.csdn.net/qq_30379689/article/details/80518283 字符串常量池 JVM为了减少字符串对象的重复创建,其内部维护了一个特殊的内 ...
- Java中的字符串常量池
ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...
- Java中的字符串常量池?
参考:http://droidyue.com/blog/2014/12/21/string-literal-pool-in-java/index.html
- java中的compareto方法的详细介绍
java中的compareto方法的详细介绍 Java Comparator接口实例讲解(抽象方法.常用静态/默认方法) 一.java中的compareto方法 1.返回参与比较的前后两个字符串的as ...
- Java String:字符串常量池(转)
作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么? 字符串常量池的设计思想是什么? 字符串常量池在哪里? 如何操作字符串常量 ...
随机推荐
- Visualbox在UEFI模式下无法正常引导
引子 前几天VMware Workstation Pro 14发布了,从12更新到14之后,很多的虚拟机开启后都黑屏.实际并非黑屏,在控制台视图可以看到屏幕的缩略图是正常显示的.目前还没有找到解决方案 ...
- CF 991
843名... 正规比赛肯定要掉分了...... 就算C没WA也是765名...为什么会这么菜呢? A,水.我加了两个特判. B,水.以 n * 4.5 为目标即可. C,裸二分,可耻的WA了一次是为 ...
- JAVA8给我带了什么——lambda表达
这此年来我一直从事.NET的开发.对于JAVA我内心深处还是很向往的.当然这并不是说我不喜欢.NET.只是觉得JAVA也许才是笔者最后的归处.MK公司是以.NET起家的.而笔者也因为兄弟的原因转行.N ...
- 【洛谷P5020】货币系统 完全背包
题目大意:给定 N 个数,求在这 N 个数中至少选出几个数能表示出所有数字,输出最少的个数. 题解:由于只有小的数字可以表示大的数字,因此首先需要对这 N 个数字进行从小到大排序.排序之后就变成一道不 ...
- bug8 eclipse项目导入到myeclipse时 Target runtime com.genuitec.runtime.generic
1.新导入的工程,出问题很大可能是jdk的版本问题导致,检查一下,发现jdk果然不一致,修改了jdk版本,但异常没有消除 2.网上查询下解决方案,原来在工程目录下的settings,有个文件也需要修改 ...
- apigateway-kong(六)认证
到上游服务(API或微服务)的流量通常由各种Kong认证插件的应用程序和配置来控制.由于Kong的服务实体(Service Entity)代表自己的上游服务的1对1映射,最简单的方案是在选择的服务上配 ...
- My thoughts after NOIP 2018(2)
又一次陷入迷茫了呢. - 大概是因为是因为自招政策要改变了吧? - 大概是因为前路在一点一点变得难走吧? - 大概是因为OI和学习实在太难平衡了吧? 未来的一切都已经不再在我控制的范围之内,不迷茫才怪 ...
- linux command ------ ls
-rw-r--r-- [d]: content [-]: file [l]: link file [b]: interface device for storage in a device file ...
- nginx-1.12.1编译参数详情
一下nginx-1.12.1编译参数详情 #/usr/local/nginx/sbin/nginx -V nginx version: nginx/1.12.1 built by gcc 4.4.7 ...
- python 小爬虫爬取博客文章初体验
最近学习 python 走火入魔,趁着热情继续初级体验一下下爬虫,以前用 java也写过,这里还是最初级的爬取html,都没有用html解析器,正则等...而且一直在循环效率肯定### 很低下 imp ...