-1

我知道java中的一个字符是2字节。但是如果在列表中加载一些字符,它们将花费 87B 来保存一个字符。测试如下:

有一个文件“源”包含 995328 行。每一行都只是一个字符:'a'。(因此在java中保存所有字符将花费近2MB)。

我的源代码中调用了两个 sleep 方法,我使用 top 命令随时检查内存使用情况。

运行第一个 sleep(10000) 方法时的 RSIZE 值为 25M,运行第二个 sleep 方法时为 108M。所以每个字符串(这只是一个“a”)成本:(108MB-25MB)/995328=87B。我不知道为什么一个字符串“a”要花这么多内存!!!谁能告诉我为什么?

public static void main(String[] args) throws Exception{
    File file = new File("source");
    BufferedReader br = new BufferedReader(new FileReader(file));
    String line = null;
    List<String> list = new ArrayList<String>();
    Thread.sleep(10000); 
    while((line = br.readLine())!=null){
        list.add(line);
    }
    Thread.sleep(10000);

}
4

5 回答 5

3

@Amir 说得对,有比 top 更好的方法(例如, JDK 中包含hprof)来测量内存使用情况,但是有一些更深层次的问题会混淆您的内存数量。

  1. 您没有关闭filebr。这是最大的。这些对象中的每一个都是围绕一堆本机代码的包装器,用于与操作系统的文件 I/O 库进行交互。这些资源包括文件句柄和缓存缓冲区,因此您从文件中读取的一些数据在内存使用中被计算两次——一次在附加到的缓存中br,一次在list.
  2. 每个字符串实际上不仅仅是一个字符序列。JRE 维护一个指向字符数组的指针、一个起始索引和一个长度,以及其他数据。指向字符数组的指针为 8 个字节,起始索引为 4 个字节,长度为 4 个字节。我确定我遗漏了一些字段,但即使是这个保守的估计也会为字符串提供 16 个字节的开销,而忽略字符串中的实际字符。
  3. list变量也有开销。有一个后备数组,其中每个插槽都是一个指针(多 8 个字节),并且有很多空插槽。随着后备数组的增长以容纳行,ArrayList该类会留下一些额外的空间,因为调整数组大小(即创建一个新数组并复制旧数组中的所有元素)很昂贵,并且每个空槽是 8 64 位系统上的字节数。
  4. top 返回的数字包括垃圾。垃圾收集器因 JVM 实现和版本而异,但通常它会快速收集新对象,并且仅在存在内存压力时才收集旧对象。因此,调整后备存储大小后剩下的所有额外数组ArrayList很可能仍在内存中,并计入最高数字。由于这些数组一开始就很大(很可能有一个至少有 500K 个插槽,每个插槽都有一个 8 字节的指针),这会增加程序的总内存使用量。

注意我在上面谈到了 8 字节指针,假设是 64 位系统。在 32 位系统上,我所说的一切都成立,除了指针只有 4 个字节。

于 2012-04-13T05:56:34.137 回答
2

我不会依靠 top 来计算这些数字。你为什么不使用像VisualVM这样的东西——它会准确地告诉你数据结构占用了多少内存?

RSIZE我相信反映了总常驻内存,其中包括 JVM 本身使用的内存!除了这个问题,您的基准测试没有考虑 JVM 尚未收集的无法访问的对象。使用分析器的堆快照会触发 GC,它确实考虑到了这一点。

于 2012-04-13T05:48:04.173 回答
1

您不仅在 arraylist 中保存字符,而且String在文件中每行存储一个实例。

我自己没有做过这些计算,但根据Neil Coffeys 关于字符串内存利用率的教程,每个字符串占用:

最小字符串内存使用量(字节)= 8 * (int) ((((无字符) * 2) + 45) / 8)

如果您的文件每行包含一个字符,则每个字符串将花费您至少8*((2+45) / 8)= 47 个字节。

再加上 arraylist 的成本。

于 2012-04-13T05:56:43.417 回答
0

您完全忽略了您正在创建的字符串和列表的成本,特别是它的增长策略。检查Javadoc。我在里面看到的 ArrayList 的实现在列表溢出时会增加 50%。

于 2012-04-13T05:54:00.097 回答
0

Java 是一种垃圾收集语言,因此您无法查看外部测量的虚拟内存占用量在执行某些代码时的变化来估计数据结构的大小。您正在考虑堆中的增量,这可能是由于垃圾的积累以及垃圾的积累。如果给自己提供比表示活动对象集所需的空间多得多的空间,那么垃圾收集也会更好,这样收集就不会那么频繁。一般来说,如果闲置空间很少,垃圾收集会变慢。如果虚拟机将其内存占用量保持在表示所有对象所需的最低限度,它的性能将非常糟糕。

于 2012-04-13T06:06:04.333 回答