文档章节

String Interning研究

crossbell
 crossbell
发布于 2014/06/04 00:46
字数 1288
阅读 11
收藏 0

对Java字符串的探究

SEP 8TH, 2013 | COMMENTS

问题的出发点

在网上看到一道题:

1
String str = new String("abc");

以上代码执行过程中生成了多少个 String 对象?

答案写的是两个。”abc”本身是一个,而 new 又生成了一个。

“abc”是什么

查看这句程序的字节码,如下:

12345
NEW String  DUP  LDC "abc"  INVOKESPECIAL String.<init>(String) : void  ASTORE 1

指令ldc indexbyte的含义:将两字节的值从 indexbyte 索引的常量池中的项中推到方法栈上。

指令LDC "abc"说明了”abc”并不是直接以对象存在的,而是存在于常量池的索引中。String 的构造函数调用命令实际使用的就是 String 类型作为参数,那么,栈上应该有一个 String 类型的索引。

由此我们得出,在字节码中,ldc 命令在常量池中找到了能索引到“abc”那个 String 对象的索引值。

常量池

常量池是类文件(.class)文件中的一部分,记录了许多常量信息,索引的字符串信息。

由于 Java 是动态加载的,类文件并没有包含程序运行时的内存布局,方法调用等无法直接记录出方法的物理位置,常量池通过索引的方法解决了这个问题。

常量池中存着许多表,其中 Constant_Utf8_info 表中,记录着会被初始化为 String 对象的字符串的字面值(iteral)。 而在 String 的 java doc 中,有对 String 字面值的说明:

All string literals in Java programs, such as “abc”, are implemented as instances of this class.

在 Java 编译的过程中,确定下来的 String 字面值都先被优化记录在常量池中(那些双引号字符串,都是以 CONSTANT_utf8_info 的形式存储在常量池中的)。也就是说,Java 源代码文件中出现的那些诸如”abc”字符串,都已经被提前放在了常量池中。

可以使用如下代码验证这一点:

123456789
public class Program {  public static void main(String[] args)  {  String str1 = "Hello";  String str2 = "Hello";  System.out.print(str1 == str2);  } }

输出结果是 true.说明”Hello”作为对象是被程序从同一个内存空间读取出来的。

常量池是编译时产生的,存在于类文件中(*.class 文件)。运行时,JVM 中每个对象都拥有自己的运行时常量池(run time constant pool)。

字符串池

我在 String 的 java doc 中又发现了一个有趣的 method:intern() ,我翻译如下:

当 intern 方法被调用,如果池中已经拥有一个与该 String 的字符串值相等(即 equals()调用后为 true)的 String 对象时,那么池中的那个 String 对象会被返回。否则,池中会增加这个对象,并返回当前这个 String 对象。

其中有介绍一个字符串池的东西:字符串池(String pool),初始是空的,由类私有的控制。

查看 java.lang.String 的源代码,发现 Intern()方法是一个 native 方法,即本地实现的方法,而不是一个 java 方法,这让我们不能直观的看到字符串池的实现细节。不过能够理解字符串池其实是类似于线程池的缓冲器,可以起到节约内存的作用。如下代码可以验证

12345678910111213
package biaobiaoqi.thinkingInJava;  public class Test {  public static void main(String[] args){   String strA1 = "ab";  String strA2 = "c";  String strB1 = "a";  String strB2 = "bc" ;  System.out.println((strA1+strA2).intern() == (strB1 + strB2).intern());   } }

输出结果为 true。

现代的 JVM 实现里,考虑到垃圾回收(Garbage Collection)的方便,将 heap 划分为三部分: young generation 、 tenured generation(old generation)和 permanent generation( permgen )

字符串池是为了解决字符串重复的问题,生命周期长,它存在于 permgen 中。

总结

编译 Java 源代码时,源文件中出现的双引号内的字符串都被收纳到常量池中,用 CONSTANT_utf8_info 项存储着。

JVM 中,相应的类被加载运行后,常量池对应的映射到 JVM 的运行时常量池中。其中每项 CONSTANT_utf8_info(也就试记录那些字符串的)都会在常量引用解析时,自动生成相应的 internal String,记录在字符串池中。

回过头来看看文章刚开始的那个问题。

1
String str = new String("abc");

这里确实是有两个 String 对象生成了。

new String("xxx") 创建的 String 对象会在 heap 中重新生成新的 String 对象,绕过字符串池的管辖。而如果使用String str = "xxx"则先查看字符串池 是否已经存在,存在则直接返回 PermGen 中的该 String 对象,否则生成新的 String 对象,并将它加入字符串池中。

尽量使用String str = "abc";,而不是String str = new String("abc");用 new 的方法肯定会开辟新的 heap 空间,而前者的方法,则会通过 string interning 优化。

参考资料

 原文地址:http://biaobiaoqi.github.com/blog/2013/09/08/string-interning/
 版权声明:自由转载-非商用-非衍生-保持署名| Creative Commons BY-NC-ND 3.0

本文转载自:http://blog.csdn.net/biaobiaoqi/article/details/6892352

共有 人打赏支持
crossbell
粉丝 25
博文 172
码字总数 14545
作品 0
海淀
项目经理
私信 提问
C++中简单实现foreach循环

看别人用Qt写的程序时,偶然发现虽然程序中用到了foreach。foreach本来不是C++的关键字,但Qt是如何实现的呢?查看Qt的文档,里面说是用preprocesser(预处理)实现的。研究了一下,原来是宏定...

日久不生情
2017/11/14
0
0
emacs lisp 研究 lisp.h 继续 (几何画板开发笔记 七)

粗略地研究了 LispCons 结构之后,建议研究下一个重要的结构 LispSymbol: struct LispSymbol { unsigned gcmarkbit : 1; // gc 标记位,与 gc 相关以后详述。 enum symbolredirect redirect...

刘军兴
2014/05/12
0
0
蛋白质互做数据库(string)

1. String 介绍 研究一个基因及其编码的蛋白质,一方面要了解它们的功能,另一方面需研究此蛋白质相互作用的其他蛋白质的信息,以使研究人员能够更加深入地认清相关蛋白质的功能,更清楚地理...

thinkando
05/21
0
0
MATLAB 类型转换(转载)

int转string:int2str(m) double转string:num2str(m) 在matlanb中help uint8,就可以看到matlab中常用的数据类型,如double, single, datatypes, isinteger, uint16, uint32, uint64, int8, i......

Yong_Luo
2010/05/15
19.9K
0
研究学习Kotlin的一些方法

Kotlin是一门让人感到很舒服的语言,相比Java来说,它更加简洁,省去了琐琐碎碎的语法工作,同时了提供了类似Lambda,String template,Null Safe Operator等特性。让开发者用起来得心应手。 ...

技术小黑屋
2017/05/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

ActiveMQ消息传送机制以及ACK机制详解

AcitveMQ是作为一种消息存储和分发组件,涉及到client与broker端数据交互的方方面面,它不仅要担保消息的存储安全性,还要提供额外的手段来确保消息的分发是可靠的。 一. ActiveMQ消息传送机...

watermelon11
20分钟前
1
0
HashTable和Vector为什么逐渐被废弃

HashTable,不允许键值为null,还一个就是put方法使用sychronized方法进行线程同步,单线程无需同步,多线程可用concurren包的类型。 如编程思想里面说的作为工具类,封闭性做的不好没有一个...

noob_chr
昨天
0
0
Win10 下安装Win7双系统

很多人买了预装64位Win8/8.1的电脑后想重装(或者再安装一个)Win7系统,但是折腾半天发现以前的方法根本不奏效。这是因为预装Win8/8.1的电脑统一采用了UEFI+GPT引导模式,传统的BIOS(Legacy...

yaly
昨天
2
0

中国龙-扬科
昨天
2
0
假若明天来临——《AI.未来》读后感3900字

假若明天来临——《AI.未来》读后感3900字: 你有没有想过,如果有一天你被确诊为癌症患者,你会做些什么?你有没有想过,在你百年之后,你希望你的墓碑上刻写着什么内容? 在我翻开李开复老...

原创小博客
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部