java:为什么一个字符要花费这么多内存?

java:为什么一个字符要花费这么多内存?,java,Java,我知道java中的字符是2字节。但如果在列表中加载一些字符,则保存一个字符将花费87B。测试如下所示: 有一个文件“source”包含995328行。每一行都只是一个字符:“a”。(因此,在java中节省所有字符将花费近2MB) 在我的源代码中调用了两个sleep方法,我使用top命令随时检查内存使用情况 运行第一个睡眠(10000)方法时的RSIZE值为25M,运行第二个睡眠方法时的RSIZE值为108M。因此,每个字符串(这只是一个“a”)的成本:(108MB-25MB)/995328=87

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

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

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

运行第一个睡眠(10000)方法时的RSIZE值为25M,运行第二个睡眠方法时的RSIZE值为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);

}
publicstaticvoidmain(字符串[]args)引发异常{
文件=新文件(“源”);
BufferedReader br=新的BufferedReader(新文件读取器(文件));
字符串行=null;
列表=新的ArrayList();
睡眠(10000);
而((line=br.readLine())!=null){
列表。添加(行);
}
睡眠(10000);
}

我不会依赖top来计算这些数字。为什么不使用类似-的东西来准确地告诉您数据结构占用了多少内存


RSIZE
我相信它反映了总的驻留内存,包括JVM本身使用的内存!除此之外,基准测试没有考虑JVM尚未收集的不可访问对象。使用探查器的堆快照会触发GC,GC会考虑到这一点。

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

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

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

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

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

    我自己还没有做过这些计算,但据我所知,每个字符串都会占用:

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

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


    再加上arraylist的成本。

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

    您如何知道
    char
    需要87字节(听起来不太合理)?你是用什么方法来衡量的?还要注意,单个
    字符
    与包含一个字符的
    字符串
    对象完全不同。