如何使用字符串有效地管理内存?

时间:2014-04-30 05:32:40

标签: java string performance memory-leaks jvm

考虑一段示例代码。

public void testString()
{     
    int i = 0;
    while(i < 100000000)
    {
        String s ="Hi hello bye" +i;
        i++;          
    }
}

在每次迭代中,都会创建一个新的String,并且下一次迭代不再需要它的值。我尝试打印之前消耗的内存并发布此testString()功能。这是他们的价值观。

Before invoking testString():

Total Memory: 91684864 (87.4375 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  72163552 (68.82052612304688 MB)

After invoking testString():
Total Memory: 424280064 (404.625 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  171766816 (163.80960083007812 MB).

我看到使用了大量内存,并且由于当前处理字符串的方式,我担心JVM堆可能会超出范围。 在迭代2中不再需要为迭代1生成的字符串,并且可以释放其存储空间。我相信这不会发生在这里。

我尝试使用StringBuffer和StringBuilder对象,内存使用量似乎有了很小的改善。

请帮助我提供一个更好,更优化的方法。

7 个答案:

答案 0 :(得分:50)

  

迭代2中不再需要为迭代1生成的字符串,并且可以释放其存储空间。我相信这不会发生在这里。

肯定会发生

您创建了1亿个字符串,每个字符串至少包含13个字符 - 其中大部分字符串大约为20个字符。每个字符串由一个对象(有开销)和一个char[]组成 - 所以我猜它占用大约60个字节的20个字符的字符串。

如果垃圾收集无效,那么每个需要60个字节的1亿个对象需要6GB - 而你看到的总内存只比开始时大约300MB。

正在收集字符串 - 只是立即

您还没有告诉我们您需要对真正的代码中的字符串做什么(我假设有一个真正的动机) - 假设您在每次迭代中确实需要一个字符串一个循环,我不认为使用StringBuilder会帮助你。如果需要数据为StringBuilder,那么您可以提高效率,但很少创建StringBuilder但不要调用{{1}在它上面。

答案 1 :(得分:7)

第一次运行会发生什么

JVM运行代码,生成字符串,垃圾收集器以一定的时间间隔释放已用内存。除了一些浪费的执行时间,程序将正常运行。

如果频繁调用该函数会发生什么

JVM将开始优化循环,意识到没有对这些字符串做任何事情,并将整个函数标记为死代码。最终调用该函数几乎没有任何意义,因为JVM将内容转换为简单的return

  

请帮助我提供一个更好,更优化的方法。

因为即使JVM也不知道你的代码应该做什么......你想做什么?对于您当前的实际问题,可能有一个最佳解决方案,这与您最初发布的代码示例非常不同。

答案 2 :(得分:3)

这取决于你如何使用StringBuilder。此

    StringBuilder sb = new StringBuilder("");
    while (i < 100000000) {
        sb.delete(0, sb.length());
        sb.append("Hi hello bye").append(i);
        i++;
    }

在内存消耗和速度方面都会更有效率

答案 3 :(得分:3)

字符串是不可变的,当你要连续添加一个字符串时,它会为每次迭代创建一个字符串对象,因此内存已超出。 如果你使用StringBuilder,它在单个对象中工作,即使它处理多个迭代也会发生。 StringBuilder是可变的。

StringBuilder iter = new StringBuilder("");
while (i < 100000000) 
{
    iter.delete(0, iter.length());
    iter.append("Hi hello bye").append(i);
    i++;
} 

答案 4 :(得分:3)

JVM永远不会从未引用的对象(例如示例程序中的字符串)中耗尽堆内存,因为在它抛出OutOfMemory异常之前,它将运行垃圾收集。从Java Virtual Machine Specification, section 6.3

中查看此异常的语义
  

OutOfMemoryError:Java虚拟机实现已用完虚拟或物理内存,并且自动存储管理器无法回收足够的内存来满足对象创建请求。

答案 5 :(得分:3)

字符串构建器的使用是您拥有的最佳选择。由于您所需的字符串/字符串构建器数量巨大,因此您无法期望JVM在任何情况下都可以避免使用大量内存。 请参阅上面的统计信息:

使用字符串:总内存的可用内存百分比为40.48% ((163.80960083007812 MB/404.625 MB )*100)

使用字符串构建器:总内存的可用内存百分比为69.35 % ((252.659 MB/364.3125 MB)*100),这是一个非常显着的改进。此外,上述变量的使用仅在循环范围内,因此JVM的垃圾收集器将在需要时运行以清除内存。

答案 6 :(得分:2)

由于变量的范围仅适用于while循环的迭代,所以在这里你不需要担心内存溢出,因为在下一次垃圾收集器执行时它将释放所有内存:

while(i < 100000000)
{
    String s ="Hi hello bye" +i;
    i++;          
}// no more required the s afterward

在每次迭代中,String会创建一个新对象,但现在不需要上一个,所以它在内存中,直到垃圾收集器清理它。