- 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.
- 計(jì)算思維與算法入門
- ClickHouse性能之巔:從架構(gòu)設(shè)計(jì)解讀性能之謎
- 案例式C語(yǔ)言程序設(shè)計(jì)
- 人人都是網(wǎng)站分析師:從分析師的視角理解網(wǎng)站和解讀數(shù)據(jù)
- Mastering macOS Programming
- Webpack實(shí)戰(zhàn):入門、進(jìn)階與調(diào)優(yōu)
- MySQL入門很輕松(微課超值版)
- JavaScript應(yīng)用開發(fā)實(shí)踐指南
- Nagios Core Administration Cookbook(Second Edition)
- UI設(shè)計(jì)基礎(chǔ)培訓(xùn)教程(全彩版)
- HikariCP數(shù)據(jù)庫(kù)連接池實(shí)戰(zhàn)
- HTML5 Canvas核心技術(shù):圖形、動(dòng)畫與游戲開發(fā)
- 讓Python遇上Office:從編程入門到自動(dòng)化辦公實(shí)踐
- Java程序設(shè)計(jì)
- 基于JavaScript的WebGIS開發(fā)