我正在使用VisualVM监视大型Java应用程序的性能和CPU。 当我查看其内存配置文件时,我发现char数组正在使用最大堆(大约50%)。
以下是内存配置文件的屏幕截图:
在任何给定时间的内存配置文件中,我都会看到大约9000个char []对象。
应用程序接受一个大文件作为输入。该文件大致有大约80行,每行包含15-20个分隔的配置选项。应用程序解析文件并将这些行存储在字符串的ArrayList中。然后它解析这些字符串以获取每个服务器的各个配置选项。
应用程序还经常将每个事件记录到控制台。
Strings的Java实现在内部使用char []以及对数组的引用和3整数。
从互联网上的不同帖子看,StringBuffer,StringBuilder,String.intern()似乎是内存效率更高的数据类型。
他们如何与java.lang.String进行比较?有没有人对它们进行基准测试?如果应用程序使用多线程(它确实如此),它们是安全的替代方案吗?
答案 0 :(得分:1)
我所做的是拥有一个或多个字符串池。我这样做是为了a)如果我在池中有一个字符串而没有创建新的字符串,并且b)减少保留的内存大小,有时减少3-5倍。您可以自己编写一个简单的字符串,但我建议您先考虑如何读取数据以确定最佳解决方案。这很重要,因为如果你没有有效的解决方案,你可能会更加容易。
正如EJP指出一次处理一行更有效,就像在阅读时解析每一行一样。即int
或double
占用的空间远远小于相同的字符串(除非您的复制率非常高)
这是一个StringInterner的示例,它使StringBuilder避免不必要地创建对象。首先使用文本填充循环使用的StringBuilder,如果匹配该文本的String在interner中,则返回该String(或StringBuilder的toString()。)好处是您只创建对象(并且不超过当你看到一个新的String(或至少一个不在数组中)时,这可以获得80%到99%的命中率,并在加载许多数据串时显着减少内存消耗(和垃圾)。
public class StringInterner {
@NotNull
private final String[] interner;
private final int mask;
public StringInterner(int capacity) {
int n = nextPower2(capacity, 128);
interner = new String[n];
mask = n - 1;
}
@Override
@NotNull
public String intern(@NotNull CharSequence cs) {
long hash = 0;
for (int i = 0; i < cs.length(); i++)
hash = 57 * hash + cs.charAt(i);
int h = hash(hash) & mask;
String s = interner[h];
if (isEqual(s, cs))
return s;
String s2 = cs.toString();
return interner[h] = s2;
}
static boolean isEqual(@Nullable CharSequence s, @NotNull CharSequence cs) {
if (s == null) return false;
if (s.length() != cs.length()) return false;
for (int i = 0; i < cs.length(); i++)
if (s.charAt(i) != cs.charAt(i))
return false;
return true;
}
static int nextPower2(int n, int min) {
if (n < min) return min;
if ((n & (n - 1)) == 0) return n;
int i = min;
while (i < n) {
i *= 2;
if (i <= 0) return 1 << 30;
}
return i;
}
static int hash(long n) {
n ^= (n >> 43) ^ (n >> 21);
n ^= (n >> 15) ^ (n >> 7);
return (int) n;
}
}
这个类很有意思,因为它在传统意义上不是线程安全的,但在并发使用时会正常工作,实际上当多个线程拥有不同的数组内容视图时可能会更有效。