Java中的哈希表内存使用情况

use*_*771 2 java memory-leaks hashtable

我使用java从文件中读取数据,将数据复制到较小的数组并将这些数组放在Hashtables中.我注意到Hashmap比原始文件消耗更多内存(大约两倍)!知道为什么吗?

这是我的代码:

public static void main(final String[] args) throws IOException {
    final PrintWriter writer = new PrintWriter(new FileWriter("test.txt",
            true));
    for(int i = 0; i < 1000000; i++)
        writer.println("This is just a dummy text!");
    writer.close();

    final BufferedReader reader = new BufferedReader(new FileReader(
            "test.txt"));
    final HashMap<Integer, String> testMap = new HashMap<Integer, String>();
    String line = reader.readLine();
    int k = 0;
    while(line != null) {
        testMap.put(k, line);
        k++;
        line = reader.readLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

rol*_*lve 6

这不是问题HashMap,它一般是Java对象的问题.每个对象都有一定的内存开销,包括数组和你的条目HashMap.

但更重要的是:字符数据占用内存空间的两倍.原因是Java为每个字符使用16位,而文件可能以ASCII或UTF-8编码,每个字符仅使用7或8位.

更新:你无能为力.您发布的代码原则上很好.它只适用于大文件.如果HashMap仔细调整,可能会做得更好,或者您可以使用字节数组而不是字符串来存储字符(假设所有内容都是ASCII或单字节UTF-8).

但最终,要解决内存不足的问题,正确的方法是重新考虑您的程序,这样您就不必立即将整个文件读入内存.

无论你正在使用该文件的内容做什么,想一想你是否可以在从磁盘读取文件时这样做(这称为流式传输),或者可能提取相关部分并仅存储这些部分.您也可以尝试随机访问该文件.

我建议你仔细阅读这些内容,尝试一下并回来问一个特定于你的申请的新问题.因为这个帖子太长了.


ass*_*ias 6

地图是一种"可扩展"结构 - 当它达到其容量时,它会被调整大小.因此,有可能说地图使用的40%的空间实际上是空的.如果您知道地图中有多少条目,则可以使用特殊构造函数以最佳方式调整地图大小:

Map<xx,yy> map = new HashMap<> (length, 1);
Run Code Online (Sandbox Code Playgroud)

即使您这样做,地图仍将使用比所包含项目的实际大小更多的空间.

更详细地说:HashMap的大小在达到(capacity*loadFactor)时会增加一倍.HashMap的默认加载因子是0.75.

例:

  • 想象一下,您的地图的容量(大小)为10,000个条目
  • 然后在地图中放入7,501个条目.容量*loadFactor = 10,000*0.75 = 7,500
  • 因此,您的hashmap已达到其调整大小阈值并被调整大小为(capacity*2)= 20,000,尽管您只持有7,501个条目.这浪费了很多空间.

编辑

这个简单的代码让您了解实践中发生了什么 - 输出是:

threshold of empty map = 8192
size of empty map = 35792
threshold of filled map = 8192
size of filled map = 1181712
threshold with one more entry = 16384
size with one more entry = 66640
Run Code Online (Sandbox Code Playgroud)

这表明如果您添加的最后一个项目强制地图调整大小,则可以人为地增加地图的大小.不可否认,这并不能说明您所观察到的整体效果.

public static void main(String[] args) throws java.lang.Exception {
    Field f = HashMap.class.getDeclaredField("threshold");
    f.setAccessible(true);

    long mem = Runtime.getRuntime().freeMemory();
    Map<String, String> map = new HashMap<>(2 << 12, 1); // 8,192
    System.out.println("threshold of empty map = " + f.get(map));
    System.out.println("size of empty map = " + (mem - Runtime.getRuntime().freeMemory()));

    mem = Runtime.getRuntime().freeMemory();
    for (int i = 0; i < 8192; i++) {
        map.put(String.valueOf(i), String.valueOf(i));
    }
    System.out.println("threshold of filled map = " + f.get(map));
    System.out.println("size of filled map = " + (mem - Runtime.getRuntime().freeMemory()));

    mem = Runtime.getRuntime().freeMemory();
    map.put("a", "a");
    System.out.println("threshold with one more entry = " + f.get(map));
    System.out.println("size with one more entry = " + (mem - Runtime.getRuntime().freeMemory()));
}
Run Code Online (Sandbox Code Playgroud)