5
{
    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.

4

2 回答 2

6

使用时,StringBuilder您要保留空间以将字符串添加到缓冲区。这是一个很好的示例,StringBuilder而不是字符串,因为您使用的是 while 循环。

当你使用 aString时,每次你输入如下内容:

String s = s + someOtherString;

您正在丢弃现有s字符串并创建一个新字符串而不是s + someOtherString. 这意味着您不断需要新的内存来制作连接的字符串,将旧的扔掉,并将变量的引用放入新的字符串。

使用 aStringBuilder您可以附加到您的字符串,而无需删除现有部分。

所以是的,它使用了更多的内存,但在某些情况下,与仅使用字符串相比,它非常有效。

换句话说:String是一个不可变的对象,而 aStringBuilder不是。


在您的代码中:

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

这与new String("\r\n" + currLine);which is 1 String 对象相同

您正在使用 1 个追加。

在您的评论中,您说如果您使用它:

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

这与new String("\r\n");2new String(currLine);个 String 对象相同

你得到的内存几乎翻了一番。这是有道理的,因为您正在执行 2x append,因此使用了两倍的内存。

于 2013-07-05T11:11:00.787 回答
1

我想写这个作为对@JRENs 答案的评论,但我没有足够的代表。2 追加占用更多内存的原因在这里http://docs.oracle.com/javase/tutorial/java/data/buffers.html

在 StringBuilder 上不可用的主要操作是 append() 和 insert() 方法,它们被重载以便接受任何类型的数据。每个都将其参数转换为字符串,然后将该字符串的字符附加或插入到字符串构建器中的字符序列中。

因此,每个追加操作都会创建一个新字符串,这就是为什么 2 次追加(每个循环)占用的内存大约是一次追加的两倍。

于 2013-07-05T11:36:05.240 回答