Java String是不可变的,所以
当你创建一个字符串时,会在堆中为它分配一块内存,当你更改它的值时,会为该字符串创建一个新的内存块,而旧的内存块就有资格进行垃圾收集,示例
String str = func1_return_big_string_1()"; //not literal
String str= func2_return_big_string_2()"; //not literal
但是由于垃圾收集需要时间才能启动,因此我们实际上在堆中包含大字符串1和1的内存。如果发生这种情况,它们对我来说可能是一个问题。
有没有办法让大字符串2在字符串1的内存中使用相同的位置,所以当我们将大字符串2分配给str时,我们不需要额外的空间。
编辑: 感谢所有的输入,最后我意识到我不应该期望java代码表现得像c ++代码(即不同的内存占用)。我写了一个c ++ 11演示,它按预期工作,最大内存占用大约20M(我试图加载的最大文件),右值引用和移动赋值运算符都按预期启动。 下面的演示在VS2012中用c ++ 11完成。
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <thread>
using namespace std;
string readFile(const string &fileName)
{
ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);
ifstream::pos_type fileSize = ifs.tellg();
ifs.seekg(0, ios::beg);
vector<char> bytes(fileSize);
ifs.read(&bytes[0], fileSize);
return string(&bytes[0], fileSize);
}
class test{
public:
string m_content;
};
int _tmain(int argc, _TCHAR* argv[])
{
string base("c:\\data");
string ext(".bin");
string filename;
test t;
//std::this_thread::sleep_for(std::chrono::milliseconds(5000));
cout << "about to start" << endl;
for(int i=0; i<=50; ++i) {
cout << i << endl;
filename = base + std::to_string(i) + ext;
//rvalue reference & move assignment operator here
//so no unnecessary copy at all
t.m_content = readFile(filename);
cout << "szie of content" << t.m_content.length() << endl;
}
cout << "end" << endl;
system("pause");
return 0;
}
答案 0 :(得分:2)
使用StringBuffer,StringBuffer.append()
答案 1 :(得分:2)
我看到了几个选项:
char[]
。 StringBuilder
复制到您的版本MyStringBuilder
中。它的主要缺点是缺乏正则表达式。这就是我在提高性能时所做的事情。String
复制到MutableString
。我不认为添加自定义正则表达式匹配器会有问题,因为有很多可用的。答案 2 :(得分:1)
非实习字符串应该不重要。如果开始内存不足,垃圾收集器将删除不再引用的任何对象。
Interned Strings更难收集,详见Garbage collection of String literals
编辑非实习字符串就像普通对象一样。一旦没有更多的引用,就会收集垃圾。
如果str
是指向原始字符串的唯一引用,而str
被更改为指向其他字符串,则原始字符串可用于垃圾回收。因此,您不再需要担心内存不足,因为如果需要内存,JVM将收集内存。
答案 3 :(得分:1)
为了避免在内存中同时使用旧的和新的String,您可以通过为变量分配null
来明确允许GC清理它:
String str;
str = func1_return_big_string_1();
str = null; // Now, GC can clean, when it needs extra memory for the String.
str = func2_return_big_string_2();
更新:为了支持我的主张,我写了一个测试用例,证明我是对的:http://ideone.com/BwGfSN。 代码演示了(使用Finalizer)之间的区别:
GCTest test;
// Without the null assignment
test = create(0);
test = create(1);
test = null;
System.gc();
try {Thread.sleep(10);} catch (Exception e){}
System.out.println();
// With the null assignment
test = create(2);
test = null;
test = create(3);
test = null;
System.gc();
答案 4 :(得分:1)
我刚刚找到MutableString实现。它可以在Maven Central中找到。以下是他们的JavaDoc页面的摘录:
- 可变字符串占用的空间很小 - 它们唯一的属性是后备字符数组和整数;
- 他们的方法尽可能高效:例如,如果对数组访问的限制暗示了对参数的某些限制,我们不会明确检查它,并且Bloom过滤器用于加速多字符替换;
- 他们允许您直接访问支持阵列(风险自负);
- 它们实现
CharSequence,
,因此,例如,您可以使用标准Java API将可变字符串与正则表达式匹配或拆分;- 他们实施
Appendable
,因此可以与Formatter
和类似的类一起使用;
<强>更新强>
您可以利用此Appendable
的{{1}}接口来读取几乎为零内存开销的文件(8KB,这是Java中的默认缓冲区大小)。使用Guava的CharStreams.copy,它看起来像这样:
MutableString