0
package com.zhb.jvm;

/**
 * 
 * @author zhb
 *
 */
public class RuntimeConstantPoolOOM {
    public static void main(String[] args){

        String str1 = "abc";
        System.out.println(str1.intern() == str1);    //true
        String str2 = new String("abcd");
        System.out.println(str2.intern() == str2);    //false

        String str3 =new StringBuilder("math").append("analyze").toString();
        System.out.println(str3.intern() == str3);    //true

        String str4 =new StringBuilder("computer").append("software").toString();
        System.out.println(str4.intern() == str4);    //true

        String str5 =new StringBuilder("jav").append("a").toString();
        System.out.println(str5.intern() == str5);    //false
    }

}

First of all,we can know the definiton of the intern() method. Definition for intern : When the intern method is invoked, if the pool already contains a string equal to thisString object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, thisString object is added to the pool and a reference to this String object is returned.

str1.intern == str1 is true. this is easy to understand. str2.intern() == str2 this is also easy to understand by the definition of the method. But why str3.intern() == str3 is true.in fact,I think it is false by the definition. There is a opposite thing that str5.intern() == str5 is false. I run the command in the terminal java -version java version "1.7.0_40" Java(TM) SE Runtime Environment (build 1.7.0_40-b43) Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode)

I want to acquire the correct answer.thank you very much!

C Snover
  • 17,908
  • 5
  • 29
  • 39
Edward
  • 1,239
  • 13
  • 20
  • 1
    Reopened - it's not clear how the [marked duplicate](http://stackoverflow.com/questions/14193571/how-does-java-store-strings-and-how-does-substring-work-internally) addresses the behaviour observed here... (at least, it's certainly not obvious...) – Oliver Charlesworth Sep 27 '14 at 14:58

4 Answers4

1

why str3.intern() == str3 is true

Because, as you said:

Otherwise, thisString object is added to the pool and a reference to this String object is returned.

You're in that case. The pool doesn't contain str3 (i.e. "mathanalyze") yet. So str3 is added to the pool and returned.

For str5, you're in the other case:

if the pool already contains a string equal to thisString object as determined by the equals(Object) method, then the string from the pool is returned

So, the pool already contains the string "java" when your code is executed, which is not surprising since java is, for example, the name of the top-level package of all the standard classes, and also the name of the executable used to launch the JVM. There is a huge chance that the literal string "java" is used in the code that bootstraps the application and loads classes before executing your main method.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Isn't this the opposite of what you're describing? `str3.intern() == str3` is `true` if the pool **does** contain the string. – Oliver Charlesworth Sep 27 '14 at 14:56
  • No. *if the pool already contains a string equal to thisString object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, thisString object is added to the pool and a reference to this String object is returned.*. "java" is already in the pool, so the pooled "java" is returned. "mathanalyze" is not in the pool, so the string passed to intern() is added to the pool and returned. – JB Nizet Sep 27 '14 at 14:58
  • @OliverCharlesworth : It contains it but did not contain it before the call to `intern()`. – Dici Sep 27 '14 at 14:58
  • @JBNizet: I must be tired! By my logic: If "mathanalyze" is not already in the pool, then `str3.intern()` will add it to the pool and return a *new* reference, which will not be equal to the old reference. Thus the equality should evaluate to `false`. – Oliver Charlesworth Sep 27 '14 at 15:00
  • @OliverCharlesworth : It won't return a new reference, that's what you get wrong. It would not be logical, since the principle of adding a String in the pool is to enable to have a single reference to it. – Dici Sep 27 '14 at 15:01
  • @Dici: Ah, yes, that's what I was missing. Thanks! (thus +1 to this answer) – Oliver Charlesworth Sep 27 '14 at 15:01
  • @Dici:as you say "It would not be logical, since the principle of adding a String in the pool is to enable to have a single reference to it.",but how to explain the str2.intern() == str2 is false? – Edward Sep 28 '14 at 03:18
  • @Edward: the class itself uses the string literal "abcd". This string literal is in the pool. And the code calls intern(new String("abcd")). The string passed to intern() is thus a *copy* of the literal "abcd" that is already in the pool. What is returned is the original. – JB Nizet Sep 28 '14 at 06:36
  • @JBNizet:oh,i know,but i find a interesting thing.String str6 = new StringBuilder("teacher").toString(); System.out.println(str6.intern() == str6); the result is false.I think the string pool have existed the string literal "teacher".Maybe,StringBuilder.append can create a new String that do not exist in the string pool. – Edward Sep 29 '14 at 14:08
  • That's just the same as doing new String("teacher"). It creates a copy of a string literal that is already in the pool. – JB Nizet Sep 29 '14 at 15:03
1

The way I see it, I would say that the String "java" is by default in the pool. Indeed, when you call intern() on str3, this word is not yet in the pool so it is added and the reference returned is str3 (no new object created), so the test gives true. At the contrary, "java" is already in the pool so it returns the reference of the object in the pool, which is different from the reference of str5.

Note that you will observe the same behaviour not only with "java"but also with all the single characters.

Dici
  • 25,226
  • 7
  • 41
  • 82
  • 1
    you are correct, but slightly off. There isn't anything in the pool by default; it's just that before running your code the JVM runs a lot of other code too and `java` is something that gets interned a lot sooner. But this has changed, running in `java-11` this : `String notInPool = new String(new char[] { 'j', 'a', 'v', 'a' }); String inPool = "java2"; System.out.println(new String(notInPool).intern() == notInPool); System.out.println(new String(inPool).intern() == inPool);` will only print `true` for the second one... – Eugene Sep 09 '18 at 21:39
  • Yeah that makes sense. Thanks for the fun fact about Java 11! Do you think it changed because of a stupid refactor like removing a static constant or preventing some classes from loading until they're necessary, or is there a deeper reason like a change about the behaviour of interning itself? – Dici Sep 09 '18 at 21:44
  • 1
    Ill be honest: I have absolutely no idea – Eugene Sep 09 '18 at 21:46
0

The test foo.intern() == foo is a way of measuring whether or not foo was already in the pool before intern() was called. So str3.intern() == str3 means that str3 was not already in the pool. That's all.

There is probably a constant string "java" somewhere in the runtime that was loaded into the pool before your copy, which is why str5.intern() != str5.

Here are the most obvious cases I found by grepping the OpenJDK 8 source code:

./com/sun/beans/decoder/DocumentHandler.java:        setElementHandler("java", JavaElementHandler.class); // NON-NLS: the element name
./com/sun/tools/example/debug/gui/JDBMenuBar.java:        JDBFileFilter filter = new JDBFileFilter("java", "Java source code");
./com/sun/tools/jdi/SunCommandLineLauncher.java:                "java",
./sun/launcher/LauncherHelper.java:                (progname == null) ? "java" : progname ));
./sun/rmi/server/Activation.java:                        File.separator + "bin" + File.separator + "java";
./sun/rmi/server/Activation.java:                    command[0] = "java";
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
0

As I understand there might be any runtime string "java" , which causes this issue.

    String str5 =new StringBuilder("jav").append("a").toString();
    System.out.println(str5.intern() == str5);    //false

These above two statements are only false , except any other string you want to form its passing with true.

Intern () as per Java specification.

This returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. 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. It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.

Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

Hope it will help you.

JDGuide
  • 6,239
  • 12
  • 46
  • 64