10. StringTable
10.1. String 的基本特性
- String:字符串,使用一对"“引起来表示
- String 声明为 final 的,不可被继承
- String 实现了 Serializable 接口:表示字符串是支持序列化的。
- String 实现了 Comparable 接口:表示 string 可以比较大小
- String 在 jdk8 及以前内部定义了 final char[] value 用于存储字符串数据。JDK9 时改为 byte[]
10.1.1. String 在 jdk9 中存储结构变更
官网地址:JEP 254: Compact Strings (java.net)
Motivation
The current implementation of the
String
class stores characters in achar
array, using two bytes (sixteen bits) for each character. Data gathered from many different applications indicates that strings are a major component of heap usage and, moreover, that mostString
objects contain only Latin-1 characters. Such characters require only one byte of storage, hence half of the space in the internalchar
arrays of suchString
objects is going unused.Description
We propose to change the internal representation of the
String
class from a UTF-16char
array to abyte
array plus an encoding-flag field. The newString
class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.String-related classes such as
AbstractStringBuilder
,StringBuilder
, andStringBuffer
will be updated to use the same representation, as will the HotSpot VM’s intrinsic string operations.This is purely an implementation change, with no changes to existing public interfaces. There are no plans to add any new public APIs or other interfaces.
The prototyping work done to date confirms the expected reduction in memory footprint, substantial reductions of GC activity, and minor performance regressions in some corner cases.
动机
目前 String 类的实现将字符存储在一个 char 数组中,每个字符使用两个字节(16 位)。从许多不同的应用中收集到的数据表明,字符串是堆使用的主要组成部分,此外,大多数字符串对象只包含 Latin-1 字符。这些字符只需要一个字节的存储空间,因此这些字符串对象的内部字符数组中有一半的空间没有被使用。
说明
我们建议将 String 类的内部表示方法从 UTF-16 字符数组改为字节数组加编码标志域。新的 String 类将根据字符串的内容,以 ISO-8859-1/Latin-1(每个字符一个字节)或 UTF-16(每个字符两个字节)的方式存储字符编码。编码标志将表明使用的是哪种编码。
intern 是一个 native 方法,调用的是底层 C 的方法
如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法,它会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
也就是说,如果在任意字符串上调用 String.intern 方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是 true
通俗点讲,Interned string 就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(String Intern Pool)
10.5.1. intern 的使用:JDK6 vs JDK7/8
|
|
总结 String 的 intern()的使用:
JDK1.6 中,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址
JDK1.7 起,将这个字符串对象尝试放入串池。
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址,字符串常量池底称为hashtable结构,所以这个时候就是 {字符串变量名:堆地址}
|
|
练习 1
练习 2
10.5.2 示例
1、
2、
3、
|
|
4、
10.5.3. intern 的效率测试:空间角度
我们通过测试一下,使用了 intern 和不使用的时候,其实相差还挺多的
|
|
结论:对于程序中大量使用存在的字符串时,尤其存在很多已经重复的字符串时,使用 intern()方法能够节省内存空间。
大的网站平台,需要内存中存储大量的字符串。比如社交网站,很多人都存储:北京市、海淀区等信息。这时候如果字符串都调用 intern()方法,就会很明显降低内存的大小。
10.6. StringTable 的垃圾回收
运行结果
可知现在字符串常量池中只有6万多数据,不足十万
10.7. G1 中的 String 去重操作
官网地址:JEP 192: String Deduplication in G1 (java.net)
Motivation
Many large-scale Java applications are currently bottlenecked on memory. Measurements have shown that roughly 25% of the Java heap live data set in these types of applications is consumed by
String
objects. Further, roughly half of thoseString
objects are duplicates, where duplicates meansstring1.equals(string2)
is true. Having duplicateString
objects on the heap is, essentially, just a waste of memory. This project will implement automatic and continuousString
deduplication in the G1 garbage collector to avoid wasting memory and reduce the memory footprint.
目前,许多大规模的 Java 应用程序在内存上遇到了瓶颈。测量表明,在这些类型的应用程序中,大约 25%的 Java 堆实时数据集被String'对象所消耗。此外,这些 "String "对象中大约有一半是重复的,其中重复意味着 "string1.equals(string2) "是真的。在堆上有重复的
String’对象,从本质上讲,只是一种内存的浪费。这个项目将在 G1 垃圾收集器中实现自动和持续的`String’重复数据删除,以避免浪费内存,减少内存占用。