有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

为什么Java HashMap速度变慢了?

我尝试用一个文件的内容构建一个地图,我的代码如下:

    System.out.println("begin to build the sns map....");
    String basePath = PropertyReader.getProp("oldbasepath");
    String pathname = basePath + "\\user_sns.txt";
    FileReader fr;
    Map<Integer, List<Integer>> snsMap = 
            new HashMap<Integer, List<Integer>>(2000000);
    try {
        fr = new FileReader(pathname);
        BufferedReader br = new BufferedReader(fr);
        String line; 
        int i = 1;
        while ((line = br.readLine()) != null) {
            System.out.println("line number: " + i);
            i++;

            String[] strs = line.split("\t");
            int key = Integer.parseInt(strs[0]);
            int value = Integer.parseInt(strs[1]);
            List<Integer> list = snsMap.get(key);
            //if the follower is not in the map
            if(snsMap.get(key) == null) 
                list = new LinkedList<Integer>();
            list.add(value);
            snsMap.put(key, list);
            System.out.println("map size: " + snsMap.size());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("finish building the sns map....");
    return snsMap;

程序一开始非常快,但当打印的信息是:

 map size: 1138338
 line number: 30923602
 map size: 1138338
 line number: 30923603 
 ....

我试着用两个系统来解释。出来println()子句来判断BufferedReader和HashMap的性能,而不是Java探查器。 有时在获取线号信息后需要一段时间才能获取地图大小信息,有时在获取地图大小后需要一段时间才能获取线号信息。我的问题是:是什么让我的程序变慢了?大文件的BufferedReader还是大映射的HashMap


共 (5) 个答案

  1. # 1 楼答案

    最好的方法是使用profiler(例如,JProfile)运行程序,并查看哪些部分比较慢。例如,调试输出也会降低程序的速度

  2. # 2 楼答案

    如果您是从Eclipse内部进行测试的,那么您应该知道写入stdout/stderr会对性能造成巨大的损失,因为Eclipse在控制台视图中捕获了该输出。即使在Eclipse之外,在紧密循环中打印也总是一个性能问题

    但是,如果你抱怨的是在处理了3000万行之后速度变慢,那么我敢打赌这是内存问题。首先,它由于强烈的GC’ing而减慢,然后随着OutOfMemoryError而中断

  3. # 3 楼答案

    你必须用一些分析工具来检查你的程序,以了解为什么它很慢。 一般来说,文件访问比内存中的操作慢得多(除非您在内存中受到限制,并且执行了过多的GC),所以这里读取文件的速度可能会更慢

  4. # 4 楼答案

    哈希映射并不慢,但实际上它是所有映射中速度最快的。HashTable是地图中唯一一个线程安全的,有时速度会很慢

    重要提示:读取数据后关闭BufferedReader和文件。。。这可能会有帮助

    例句:br。关闭() 文件关闭()

    请从task manager检查您的系统进程,可能有太多进程在后台运行

    有时候eclipse是真正的资源密集型的,所以试着从控制台运行它来检查它

  5. # 5 楼答案

    在你分析之前,你不会知道什么是慢的,什么不是。

    最有可能的是,System.out将显示为瓶颈,然后您将不得不再次在没有它们的情况下进行分析System.out是查找性能瓶颈所能做的最糟糕的事情,因为这样做通常会增加更糟糕的瓶颈

    对你的代码的一个明显的优化就是移动代码行

    snsMap.put(key, list);
    

    进入if语句。你只需要在创建一个新的列表时把它放进去。否则,put将用自身替换当前值

    Integer对象相关的Java开销(尤其是在Java Collections API中使用整数)在很大程度上是一个内存(因此垃圾收集!)问题有时,通过使用原始集合(如GNU trove)可以获得显著的收益,这取决于您调整代码以有效使用它们的程度。Trove的大部分好处在于内存使用。一定要尝试重写代码,使用GNU-trove中的TIntArrayListTIntObjectMap。我也会避免使用链表,尤其是对于基本类型

    粗略估计,HashMap<Integer, List<Integer>>每个条目至少需要3*16字节。双重链接列表同样需要每个存储的条目至少2*16字节。1m键+30m值~1GB。还不包括管理费用。对于GNU-trove TIntObjectHash<TIntArrayList>,每个键应该是4+4+16字节,每个值应该是4字节,所以是144 MB。两者的开销可能相似

    Trove使用更少内存的原因是,这些类型专门用于int等基本值。它们将直接存储int值,从而使用4个字节来存储每个值

    Java集合HashMap由许多对象组成。大致看起来是这样的:有Entry个对象分别指向一个键和一个值对象。由于Java中处理泛型的方式,这些对象必须是对象。在您的例子中,键将是一个Integer对象,它使用16个字节(4字节标记,4字节类型,4字节实际int值,4字节填充)AFAIK。这些都是32位系统估计值。因此HashMap中的一个条目可能需要16(条目)+16(整型键)+32(但LinkedList为空)字节的内存,这些都需要考虑垃圾收集

    如果你有很多Integer对象,它只会占用的内存是使用int原语存储所有内容的4倍。这是在Java中实现的干净OOP原则的成本