9
public static void main(String[] args) {
    String s1 = new String("aa");
    s1.intern();
    String s2 = "aa";
    System.out.println(s1 == s2);

    //wrong in JDK1.6 but true in JDK1.8
    String str1 = new String("str") + new String("01");
    str1.intern();
    String str2 = "str01";
    System.out.println(str1 == str2);

}

I run the code above with JDK1.8, and I think I will get two "falses" as result, because in my opinion, it is obvious that s1 & str1 are located in the heap, and s2 & str2 are interned in the string pool, but I got a "false" and a "true". The question comes: what causes the "true"?


Above is the primitive question. Now to identify this question is far from these called duplicated questions, I would like to talk about my new finding: the second part of the code gets a "false" result with JDK1.6 while a "true" result with JDK1.8. Some blogs say the behaviour of intern() has changed after the release of JDK1.7.

If the pool dosen't contain a string equals to this String object, this String object will not added to the pool and a reference to this String object will be added to the pool instead. It means the reference in the pool will be assigned to the string object located somewhere else(like the heap), and next time the initialization of a literal string equals the early one will also be assigned to the string object., which exactly describes the code part2 about the "true" result.

This theory does work on my way to explain result of the code above. But it is obvious that the theory is out of what the intern() doc contains, which is almost the same in JDK6/8 API.


Now the question comes as: Is there any better explaination for the different results of the same code in JDK1.6 & JDK 1.8? Is the theory I mentioned above exactly what truly to happen?

Lebecca
  • 2,406
  • 15
  • 32
  • 3
    `intern()`.....? – Maroun Mar 25 '18 at 09:10
  • 3
    If you read the documentation for the intern() method it should become clearer. – cpp beginner Mar 25 '18 at 09:14
  • More clearly? @Maroun – Lebecca Mar 25 '18 at 09:19
  • Ok, i'm glad to do that and thanks @cpp beginner – Lebecca Mar 25 '18 at 09:19
  • 4
    You shouldn't assume anything about placement of the objects. You can figure out what happens, but the point of JVM and automatic memory allocation is to isolate you from that. A compiler may do some tricks here, it may keep a string constant cache or do no optimization at all. That behaviour is not a part of language and may change at any moment without notice, so you shouldn't ever rely on it. – Filip Malczak Mar 25 '18 at 09:26
  • @FilipMalczak I don’t think your comment is correct. The fact that String literals are interned is specified in both the JLS and the documentation for the String class. It may conceivably change in a future version, but you should be able to rely on it for all existing versions of java. Whether it’s ever a good idea to rely on String interning is a different matter. – cpp beginner Mar 25 '18 at 09:41
  • 1
    Mind you that I didn't say anything about intern() method, but rather generally on memory layout. Even intern() method does not touch the topic of heap/stack allocation, but rather de-duplicates Strings by mapping them to common reference. You may rely on the API, and that method is part of that API - you cannot rely on your predictions and experiments on compiler internals. – Filip Malczak Mar 25 '18 at 09:44
  • @FilipMalczak Admire your deep thoughts on Java. It may point out a right way for me in the future, but currently the hr/inter won't skip me from these kind of questions. Thanks for your suggestion. – Lebecca Mar 25 '18 at 13:23
  • 1
    @Lebecca - to be precise, this is not only a Java issue. The same applies for any VM-based language, so you should apply the same train of thoughts when you learn and use .NET, python, NodeJS, Ruby, Smalltalk, any LISP dialect, etc. – Filip Malczak Mar 25 '18 at 15:19
  • 1
    [How much research effort is expected of Stack Overflow users?](https://meta.stackoverflow.com/questions/261592/how-much-research-effort-is-expected-of-stack-overflow-users) The answer, *"A lot. An absurd amount. More than you think you are capable of. After you have reached the end of your rope and the pain of not having the answer exceeds the vast amount of shame received by posting your question, that's when you can go ahead and ask. Because at that point, you will have done whatever research necessary to make it a good question worth asking!"* –  Mar 25 '18 at 18:25
  • Thanks for your warning, I will do more work before asking next time`` – Lebecca Mar 26 '18 at 00:59

2 Answers2

20

First let's look at the Java docs for String.intern():

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

Then let's look at your code:

    final String s1 = new String("aa");
    s1.intern();
    final String s2 = "aa";
    System.out.println(s1 == s2);

Here you create 3 String objects. The String literal "aa", which is added to the String pool, the new String("aa"), which is a copy constructed from "aa", but is a different instance, and the 2nd String literal "aa", which is taken from the pool again (so it's the same instance as the first "aa"). When you invoke intern(), the String pool already has a String equal to s1 (the literal "aa" it was constructed from), so "aa" is returned, but you don't use it and s1 remains a different instance. Thus s1 == s2 is false.

    final String str1 = new String("str")+new String("01");
    str1.intern();
    final String str2 = "str01";
    System.out.println(str1 == str2);

Here you create 6 String objects. The literals "str" and "01", which are put into the pool. Then two copies created from those literals. Then a concatenation of those two Strings, which will be str1. This concatenation is not placed into the pool immediately, but it will be placed into the pool when you invoke intern() on str1, so str1 itself is now in the pool. So when you create str2 as String literal "str01", it will be taken from the pool, and thus be the same instance as str1.

Max Vollmer
  • 8,412
  • 9
  • 28
  • 43
  • I still don't understand why the intern() makes the s1 stayed in the heap and str1 turned to the pool as you said. " s1 remains a different instance", why dose str1 change. The s1.intern() and str1.intern() are in the same pattern. Would you please explain it more clearly? I got it```thanks – Lebecca Mar 25 '18 at 09:54
  • @Lebecca I updated my answer with the Java docs for intern(). In the first case there already is a String equal to s1 in the pool, in the second case there is no String equal to str1 in the pool yet. So in the second case str1 gets added to the pool. – Max Vollmer Mar 25 '18 at 10:00
  • One thing I'd like to mention, the reason why I stick to my wrong opinion hours ago is that I still solve the question with the model of JVM in JDK1.6, when the string pool is still located in the non-heap, which misleads me to the wrong place. Thanks for your sharing and patience again. – Lebecca Mar 25 '18 at 12:31
  • But str1 in the second snippet, being final reference, points to the memory location in the heap. Intern method will only create an entry in the pool and not bind to the reference. So how it returns true? – swayamraina Mar 25 '18 at 13:14
  • 2
    As far as I can tell it is unspecified whether the second `println` outputs true or false. The string constant `"str01"` may be constructed either before or after `str1.intern()` places `"str01"` into the pool. – Daira Hopwood Mar 25 '18 at 13:47
  • 2
    The outcome of the second case depends on which string you pick. If you pick a string that is interned by the bootstrap, than it will print `false`. See for instance: https://ideone.com/0OZOtC – Jorn Vernee Mar 25 '18 at 13:49
  • @JornVernee It's something, I have tried "int", "boolean" and so on, they get "false" result. – Lebecca Mar 26 '18 at 06:24
1

String.intern() returns the a canonical representation for the string object, so you need to assign it if you want a reference to that canonical representation in the String pool.

String s1 = new String("aa");
s1.intern();

Here s1 is still the Object you declared, and is in the Heap.

You need to do

String s1Itern = s1.intern();

if you want the literal string

s1 == s2 is false
s1Itern == s2 is true

For the second case Max's Answer explains it well.

Bentaye
  • 9,403
  • 5
  • 32
  • 45