- Mastering High Performance with Kotlin
- Igor Kucherenko
- 732字
- 2021-06-25 20:55:22
String pool
The String pool is a set of String objects stored in the Permanent Generation section of the heap. Under the hood, an instance of the String class is an array of chars. Each char allocates two bytes. The String class also has a cached hash that allocates four bytes, and each object has housekeeping information that allocates about eight bytes. And if we're talking about Java Development Kit 7 (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/String.java?av=f) or lower, the String class also has offset and length fields. Since String is the most used type, the instances of the String class allocate a significant part of the heap.
To reduce the load on memory, the JVM has the String pool as the implementation of the Flyweight Design Pattern because memory space can be crucial for low-memory devices such as mobile devices.
Whenever double quotes are used to create a new instance of the String class, the JVM first looks for an existing instance with the same value in the String pool. If an existing instance is found, a reference to it is returned. Otherwise, a new instance is created in the String pool and then the reference to it is returned. When we use a constructor, we force the creation of a new instance of the String class in the heap:

This technique is called copy-on-write (COW). The point is that when a copy of the object is requested, the reference to the existing object is returned instead of creating a new one. In code, it may look like this:
fun main(vars: Array<String>) {
val cat1 = "Cat"
val cat2 = "Cat"
val cat3 = String("Cat".toCharArray())
println(cat1 === cat2)
println(cat1 === cat3)
}
The output:
true
false
Kotlin has its own kotlin.String class. It's not the same as the java.lang.String class. And kotlin.String doesn't have a constructor that takes another instance of the String class.
With the COW, when trying to modify an object through a particular reference, a real copy is created, the change is applied to it, and then the reference to the newly created object is returned. The following diagram illustrates this:

In code, it may look like this:
fun main(vars: Array<String>) {
val cat1 = "Cat"
val cat2 = cat1.plus("Dog")
println(cat1)
println(cat2)
println(cat1 === cat2)
}
And here's the output:
Cat
CatDog
false
This technique is good for creating simple and reliable code and can be very useful in a concurrent application, as you can be sure that your object won't be corrupted with another thread.
Let's look at the following example:
class User(val id: Int = 0, val firstName: String = "", val lastName: String = "")
fun main(vars: Array<String>) {
val user = User()
val building = "304a"
val query = "SELECT id, firstName, lastName FROM Building " + building + " WHERE firstName = " + user.firstName
}
Each concatenation creates a new instance of String. So many unnecessary objects are created in this code. Instead of concatenation, we should use StringBuilder or String Templates (https://kotlinlang.org/docs/reference/basic-types.html#string-templates), which uses StringBuilder under the hood but is much simpler to use:
val query = "SELECT id, firstName, lastName FROM Building $building WHERE firstName = ${user.firstName}"
But how can we put a String object into the String pool if we receive it from the outside? Here is how:
val firstLine: String
get() = File("input.txt")
.inputStream()
.bufferedReader()
.use { it.readLine() }
fun main(vars: Array<String>) {
println(firstLine === firstLine)
}
This is the output:
false
To put the value of the firstLine variable in the String pool, we have to use the intern() method. When this method is invoked, if the pool already contains a string equal to the value of the object, then the reference to the String from the pool is returned. Otherwise, this object is added to the pool and a reference to this object is returned. The intern() method is an implementation of interning. It's a method for storing only one copy of each distinct value:
fun main(vars: Array<String>) {
println(firstLine.intern() === firstLine.intern())
}
Here's the output:
true
You shouldn't abuse this method because the String pool is stored in the Permanent Generation section of the heap. And it can be collected only during major garbage collection.
- C及C++程序設計(第4版)
- Objective-C Memory Management Essentials
- SoapUI Cookbook
- Unity Shader入門精要
- 利用Python進行數據分析(原書第3版)
- Java EE 8 Application Development
- Learning Apache Cassandra
- Java零基礎實戰
- Spring技術內幕:深入解析Spring架構與設計原理(第2版)
- Mastering Adobe Captivate 7
- Java 從入門到項目實踐(超值版)
- Vue.js光速入門及企業項目開發實戰
- Microsoft Exchange Server 2016 PowerShell Cookbook(Fourth Edition)
- Laravel 5.x Cookbook
- 深入淺出Rust