Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StringBuilder uses more memory when append is used

{
    StringBuilder fileBuff = new StringBuilder();
    long before = getUsedMem();
    try {
    //Open InputStreamReader here
        while ((currLine = lnr.readLine()) != null) {
            fileBuff.append("\r\n" + currLine);
        }
     //Close streams
    } catch (Exception e) {
        e.printStackTrace();
    }
    System.out.println("usedTotal: " + (getUsedMem() - before));
}

private long getUsedMem() {
    return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}

While running the code several times, I get usedTotal ~ 14279888, but if I replace with fileBuff.append("\r\n").append(currLine) I get almost double the memory ~33264440.
Can somebody please explain the reason, as I know String concatenation also uses StringBuilder?

I: fileBuff.append("\r\n" + currLine);

      62: aload         6
  64: invokevirtual #16                 // Method java/io/LineNumberReader.readLine:()Ljava/lang/String;
  67: dup           
  68: astore_2      
  69: ifnull        99
  72: aload_1       
  73: new           #2                  // class java/lang/StringBuilder
  76: dup           
  77: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
  80: ldc           #17                 // String \r\n
  82: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  85: aload_2       
  86: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  89: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  92: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  95: pop           
  96: goto          62

II fileBuff.append("\r\n").append(currLine)

      62: aload         6
  64: invokevirtual #16                 // Method java/io/LineNumberReader.readLine:()Ljava/lang/String;
  67: dup           
  68: astore_2      
  69: ifnull        86
  72: aload_1       
  73: ldc           #17                 // String \r\n
  75: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  78: aload_2       
  79: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  82: pop           
  83: goto          62

Clearly #II should use less memory, but it doesn't. The file I am reading is 50k long.

like image 359
CristiL Avatar asked Jul 05 '13 11:07

CristiL


People also ask

Is StringBuilder memory efficient?

As for StringBuilder s, they are also mutable and memory efficient, they are the fastest in string manipulation, but unfortunately they are not thread-safe. If you take these facts into the consideration, you will always make the right choice!

What is the use of append in StringBuilder?

StringBuilder. append(boolean a) is an inbuilt method in Java which is used to append the string representation of the boolean argument to a given sequence. Parameter: This method accepts a single parameter a of boolean type and refers to the Boolean value to be appended.

How many times is the StringBuilder append method overloaded?

There are 13 various overloaded append() methods in both StringBuffer and StringBuilder classes.

What is time complexity of StringBuilder append?

It is O(1) when appending single characters. A StringBuilder is like an ArrayList. When you append a single item the cost is O(1). Appending a string is like calling addAll()--the cost is proportional to the length of the string / the number of items being added.


2 Answers

When using StringBuilder you're reserving space to add strings to your buffer. This is a good example to use StringBuilder rather than string because you're using a while loop.

When you're using a String, every time you type something like this:

String s = s + someOtherString;

You're throwing away your existing s string and creating a new one to go instead of it which is s + someOtherString. This means that you're constantly needing new memory to make the concatenated string, throw the old one out, and put the reference of your variable to your new string.

With a StringBuilder you can append to your string without having to remove the existing part.

So yes, it uses more memory, but it's very efficient in some scenario's compared to using just a string.

In other words: String is an immutable object and a StringBuilder isn't.


In your code:

fileBuff.append("\r\n" + currLine);

This is the same as new String("\r\n" + currLine); which is 1 String object

You're using 1 append.

In your comment you say that if you use this:

fileBuff.append("\r\n").append(currLine);

This is the same as new String("\r\n"); and new String(currLine); which are 2 String objects

You get almost double the memory. That makes sense because you're doing 2x append, thus using double the memory.

like image 166
JREN Avatar answered Oct 24 '22 10:10

JREN


I wanted to write this as a comment on @JRENs answer but I don't have enough rep. The reason 2 appends take more memory is here http://docs.oracle.com/javase/tutorial/java/data/buffers.html

The principal operations on a StringBuilder that are not available in String are the append() and insert() methods, which are overloaded so as to accept data of any type. Each converts its argument to a string and then appends or inserts the characters of that string to the character sequence in the string builder.

So, each append operation creates a new String, which is why 2 appends (per loop) takes roughly twice as much memory as one append.

like image 42
PunDefeated Avatar answered Oct 24 '22 10:10

PunDefeated