study hard and make progress every day

This article has included to my lot DayDayUP:github.com/RobodLee/DayDayUP warehouse, welcome Star, more articles, please go to: directory navigation

preface

String is probably the most used class in Java. There are very few Java programs that don’t use String. Creating objects in Java is very performance-intensive, and we often use the same strings, so creating all those same objects is a waste of performance. So StringTable is a special StringTable. It’s called a string constant pool, and it’s used to store string constants so that when we use the same string object, we can get it directly from the StringTable without having to recreate the object. So what does StringTable have? Let’s take a closer look at StringTables.

Some features of String

Immutability of String

Before we get to StringTable, we need to mention the immutability of strings, because StringTable is only possible if strings are immutable. When we define a string:

String s = "hello";
Copy the code

In this case, “Hello” is stored in StringTable, and s is a reference to “Hello” in StringTable.


When we change the value of s to hello world

String s = "hello";
s = "hello world";
Copy the code

Instead of pointing to “hello” instead of “hello world”, s points to a new string.


To verify that we refer to a new string instead of modifying its contents, we can print the hash value.

        String s = "hello";
        System.out.println(System.identityHashCode(s));
        s = "hello world";
        System.out.println(s.hashCode());
        s = "hello";
 System.out.println(System.identityHashCode(s)); Copy the code

As you can see, the first hash is the same as the third, and the second hash is different from the other two, indicating that it is indeed pointing to a new object rather than changing the value of the String.

So how is **String immutable? ** Let’s look at the source of the String class:


We can see from the source code, first of all, String class is final, indicating that it cannot be inherited, will not be changed by the immutable property of the subclass; Second, the bottom layer of a String is actually an array decorated with final, meaning that the value cannot point to a new array once it has been determined. An array that is final cannot point to a new array, but it can change the value of the array:


How can a String be immutable if it can be modified? Because the String class does not provide any method to modify the value of an array, the immutability of a String is due to its underlying implementation, not a final.

So why is **String designed to be immutable? ** I think it’s for security reasons. Imagine a program that references the same String in multiple places, but you might just want to change the contents of the String in one place. If the String is mutable, the contents of all the strings change. If this is in a critical situation, like transferring passwords or something, it could be a big problem. So strings are designed to be immutable.

Concatenation of strings

After saying String immutability, let’s talk about String concatenation problem, look at the following program

public static void main(String[] args) {
    String a = "hello";
    String b = " world!";
    String c = a+b;
}
Copy the code

It’s that simple a program. Do you know how it works? Let’s look at the bytecode instructions for this code:


I won’t explain what these bytecode instructions mean line by line, but let’s focus on the lines highlighted in red. If you can’t read the bytecode instructions, you can read the comments. As you can see, string concatenation is essentially calling the Append () method of StringBuilder and then calling the toString() method to return a new string.

Explained StringTable

When is a string put into a StringTable

Let’s take a quick look at StringTable. Its underlying data structure is HashTable, each element is key-value structure, using array + one-way linked list implementation.

Take a look at the following code:

public static void main(String[] args) {
 -> String a = "hello";
    String b = " world!";
    String c = "hello world!";
}
Copy the code

After class loading, strings like “hello” are simply loaded into the runtime constant pool as symbols, not as string objects. This is because Strings in Java are lazy-loaded, meaning that they are loaded when the program runs to a particular line. For example, when the program runs to the line that the arrow points to, “Hello” changes from a symbol to a string object, and then goes to StringTable and looks for the same string object, and returns the address to variable A if it does, and puts “Hello” into StringTable if it doesn’t. And then I give the address to variable A. Let’s see if this is true:

        String s1 = "hello world";
        String s2 = "hello world";
        String s3 = "hello world";
        String s4 = "hello world";
        System.out.println(System.identityHashCode(s1));
 System.out.println(System.identityHashCode(s2));  System.out.println(System.identityHashCode(s3));  System.out.println(System.identityHashCode(s4)); Copy the code

As you can see, all four string objects have the same hash value, which means that if StringTable already has the same object it points to the same object instead of pointing to a new object.

What did I do when I did new String()

When we use new String() to create a String object, it’s not the same as just saying String A = “hello”. The former is stored in heap memory and the latter in StringTable.


StringTable is actually in the heap, and I’ll explain that later. Let’s verify the above statement first:

        String a = "hello";
        String b = new String("hello");
        System.out.println(a == b);
Copy the code

Take a look at the results:


The result is clearly false, indicating that the two are not the same object. This is not the case with new String(). Each new String creates a new String in the heap, even with the same contents. Let’s say I create four strings.

String s1 = new String("hello world");
String s2 = new String("hello world");
String s3 = new String("hello world");
String s4 = new String("hello world");
Copy the code

There are four strings in the heap:


Let’s print the hash again to see if there are four objects:

System.out.println(System.identityHashCode(s1));
System.out.println(System.identityHashCode(s2));
System.out.println(System.identityHashCode(s3));
System.out.println(System.identityHashCode(s4));
Copy the code

As you can see from the results, there are really four different objects.

What the intern method does

Let’s start with a piece of code:

String s1 = new String("hello world");
String s2 = "hello world";
String s3 = s1.intern();

System.out.println(s1 == s2);
System.out.println(s2 == s3); Copy the code

So let’s see if we can figure out what the results are, and if you know the results, you know the intern method, and if you don’t know the intern method, see what I’m going to say.


The result is false and true. What does intern do?

Intern just tries to put a string into a StringTable, put it into a StringTable if it doesn’t exist and return the address in a StringTable, or return the address in a StringTable if it exists. In jdk1.6, an intern attempts to put a string object into a StringTable. If it does, it doesn’t. If it doesn’t, it makes a copy of the object and puts it into a StringTable. And returns the object in StringTable. But we won’t talk about version 1.6 here.

First we create a “Hello World” string object in the heap. S1 refers to the object in the heap. We then create a string constant object in StringTable with a value of “Hello World”, s2 refers to the object in this StringTable; Finally, we try to put the heap object pointed to by S1 into StringTable and find that it already exists, so we return the address of the string object in StringTable to S3. So s1 and S2 refer to the same object, s2 and s3 are the same object. It looks like this:


What if we changed the code slightly:

String s1 = new String("hello world").intern();
String s2 = "hello world";

System.out.println(s1 == s2);
Copy the code

The result is true. Let’s break it down: We use new String() to create a String object in the heap, and then we call its intern() method, so we look for the same String in StringTable, and if we find none, we put the String in StringTable. We then give s1 the address of the object in StringTable; On line 2, since there is no new String(), we simply look up the object in StringTable and give s2 the address. So s1 and S2 refer to the same object.


The location of the StringTable

We’ve already mentioned that StringTable is in the heap, so let’s verify that. The way to verify this is very simple, we put in a lot of strings and we run out of memory, and we look at which part of StringTable runs out of memory to see where it is.

ArrayList list = new ArrayList();
String str = "hello";
for(int i = 0; i < Integer.MAX_VALUE; i++) {    String s = str + i;
    str = s;
 list.add(s.intern()); } Copy the code

We call intern to put strings into StringTable and an ArrayList to store strings, to avoid garbage collection, because then every string will be strongly referenced and won’t be collected, and garbage collection won’t see what we want. Take a look at the results:


It is clear that a memory overflow has occurred in the heap, so that StringTable is stored in the heap. However, this is from version 1.7, which was saved in the permanent generation before.

Garbage collection for StringTable

Now that we’ve mentioned garbage collection, let’s verify that StringTable does garbage collection. The same code as above, with a slight modification:

String str = "hello";
for(int i = 0; i <10000; i++) {    String s = str + i;
    s.intern();
}
Copy the code

There are no strings in the ArrayList anymore, otherwise there would be no garbage collection if there were an overflow. To see the garbage collection process, add a few virtual machine parameters without specifying the heap size:


Run the program to see how it prints:


Since the heap is large enough that no garbage collection takes place, we now set the heap to a smaller size, to 1m:

-Xmx1m
Copy the code

Run the program again:


This time, there are multiple garbage collections because the heap is out of memory, so StringTable can also be garbage collected because it is out of memory.

StringTable underlying implementation and performance tuning

StringTable is a HashTable. What does a HashTable look like? It’s just an array plus a linked list, and each element is a key-value. When an element is stored, its key is computed using the hash function to obtain the array index and stored in the corresponding location.


For example, if you have a key-value and the key evaluates to 2 using the hash function, store the value at 2 in the array. However, if another key is used to calculate the same result using the hash function, such as 2, but 2 already has a value, this phenomenon is called hash conflict, how to solve it? Here the linked list method is used:


If the array is small but has many elements, the probability of hash collisions increases. As you all know, linked lists are not nearly as efficient as arrays, and too many hash collisions can affect performance. So in order to reduce the probability of hash conflicts, we can increase the size of the array appropriately. Each row of an array is called a bucket in a StringTable. We can increase the number of buckets to improve performance. The default number is 60013.

long startTime = System.nanoTime();
String str = "hello";
for(int i = 0; i <500000; i++) {    String s = str + i;
    s.intern();
} long endTime = System.nanoTime(); System.out.println("The time spent is:"+(endTime-startTime)/1000000 + "毫秒"); Copy the code

Use a vm parameter to specify a smaller bucket, say 2000:

 -XX:StringTableSize=2000
Copy the code

Run it:


It took 1.2 seconds. Let’s increase the number of buckets by 20,000:

 -XX:StringTableSize=20000
Copy the code

Run it:


As you can see, this time it only took 0.19 seconds, which is a significant performance improvement, showing that this does optimize StringTable. There is only one way to improve performance here, so I won’t go into it because I’m running out of space. I’ll probably write an article on StringTable performance tuning in the future.

Write in the last

This is the end of the article, there may be some mistakes on the content, we will see, after all, the level is limited. If you think my article is helpful to you, please don’t forget to like, forward, favorites, follow oh!

Wechat official account