Does Java optimize the creation of strings at runtime?

Let's assume I create the following two strings at runtime (from user input for example):

public void someMethod(String input) {

   if ( input == null ) return;

   String a = input + input;
   String b = input;

   ...

}

Is Java (and its compiler) smart enough to detect at runtime that b is contained in a and therefore it is not necessary to allocate memory for b? b could just point at a with half the length?

In other words, does Java implement a dynamic version of String.intern()?

EDIT

Considering answers made so far, my example should be:

public void someMethod(String input) {

   if ( input == null ) return;

   String a = input + input + input;
   String b = input + input;

   ...

}

Upvotes: 1

Views: 745

Answers (4)

Luke Maurer
Luke Maurer

Reputation: 8245

You're not actually creating two strings in your example: b is just a reference to input. So, to do what you're asking, Java would have to somehow go back and alter old strings when a new string is created (such as by saying input + input).

To answer your broader question, AFAIK the only way for two strings to be sharing memory (besides, as you mention, being intern()'d) is for one or both to have been created using substring(). So if you really wanted to save on memory, you could do this:

String a = input + input;
String b = a.substring(input.length());

(To be clear, this will only save memory if the value of b is being stored somewhere but input is discarded and winds up garbage-collected.)

EDIT

New and improved example for new and improved question:

String a = input + input + input;
String b = a.substring(2 * input.length());

(Note that this will always save memory over the second example in the question, since we've avoided an allocation altogether. So the previous caveat doesn't apply.)

Upvotes: 3

SimonJ
SimonJ

Reputation: 21306

As others have pointed out, b just points to the same string as input.

As for a, we can use javap to disassemble your code:

public static void someMethod(java.lang.String);
  Code:
   0:   aload_0
   1:   ifnonnull   5
   4:   return
   5:   new #4; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   12:  aload_0
   13:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  aload_0
   17:  invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   23:  astore_1
   24:  aload_0
   25:  astore_2
   26:  return

The compiler uses StringBuilder.append() to perform the concatenation, which (in Sun/OpenJDK, at least) just copies everything into a big char array - no intern()ing here. Same applies with your edit - the input string is appended 5 times in total.

I can imagine an alternate implementation of String(Builder|Buffer) could be written to maintain a chain of arrays, which would allow better reuse and efficiency in some cases.

Upvotes: 2

Federico Vera
Federico Vera

Reputation: 1357

Nop, the compiler is intelligent enough to detect String concatenation at compile time, for instance:

       String a = "hello, " + "world";
       //Will become
       String a = "hello, world";

But substring matching is a bit much to do every time you create a string. However Java tries to avoid multiple string creation (if two strings are equal), you should check String.intern()

Upvotes: 1

Ernest Friedman-Hill
Ernest Friedman-Hill

Reputation: 81684

No, this won't happen, because the String passed in as an argument already exists, and Strings are immutable. The double-length one has to be created separately, since the original array won't have room for more characters.

In the reverse case, though -- where you start out with the long string, and use substring() to create a shorter one -- the two strings will, indeed, share the same char array.

Upvotes: 2

Related Questions