为什么Java HashMap速度变慢了?
我尝试用一个文件的内容构建一个地图,我的代码如下:为什么Java HashMap速度变慢了?,java,hashmap,bufferedreader,Java,Hashmap,Bufferedreader,我尝试用一个文件的内容构建一个地图,我的代码如下: 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
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;
我尝试使用两个System.out.println()子句来判断BufferedReader和HashMap的性能,而不是Java探查器。
有时在获取线号信息后需要一段时间才能获取地图大小信息,有时在获取地图大小后需要一段时间才能获取线号信息。我的问题是:是什么让我的程序变慢了?大文件的BufferedReader还是大映射的HashMap 您必须使用一些分析工具检查您的程序,以了解其速度慢的原因。
一般来说,文件访问比内存中的操作慢得多(除非您在内存中受到限制,并且执行过多的GC),所以这里的猜测是读取文件可能会更慢。最好的方法是使用探查器(例如,JProfile)运行您的程序,并查看哪些部分比较慢。例如,调试输出也会减慢程序的速度。如果您是从Eclipse内部进行测试的,您应该知道写入stdout/stderr会造成巨大的性能损失,因为Eclipse会在控制台视图中捕获该输出。即使在Eclipse之外,在紧密循环中打印也是一个性能问题
但是,如果你抱怨的是在处理了3000万行之后速度变慢,那么我打赌这是内存问题。首先,由于频繁的GC'ing,它会减慢速度,然后会因
OutOfMemoryError
而中断。哈希映射并不慢,但实际上它是所有映射中速度最快的。HashTable是映射中唯一的线程安全的,有时速度会很慢
重要提示:读取数据后关闭BufferedReader和文件。。。这可能会有帮助
例如:br.close()
file.close()文件
请从任务管理器检查您的系统进程,可能有太多进程在后台运行
有时候,eclipse是真正的资源密集型,所以试着从控制台运行它来检查它。在您分析之前,您将不知道什么慢,什么不慢。 最有可能的是,
System.out
将显示为瓶颈,然后您将不得不再次在没有它们的情况下进行评测System.out
是查找性能瓶颈最糟糕的方法,因为这样做通常会增加更糟糕的瓶颈
对代码的一个明显优化就是移动代码行
snsMap.put(key, list);
输入if
语句。您只需要在创建新列表时放置此项。否则,put将仅用自身替换当前值
与Integer
对象相关的Java成本(尤其是在Java Collections API中使用整数)在很大程度上是一个内存问题(因此垃圾收集!)。有时,通过使用诸如之类的基本集合,您可以获得显著的收益,这取决于您如何调整代码以有效地使用它们。Trove的大部分收益来自内存使用。一定要尝试重写代码以使用GNU-trove中的TIntArrayList
和TIntObjectMap
。我也会避免使用链表,特别是对于基本类型
粗略估计,HashMap
每个条目至少需要3*16字节。双链接列表同样需要每个存储的条目至少2*16字节。1m键+30m值~1GB。还不包括间接费用。使用GNU-troveTIntObjectHash
时,每个键应为4+4+16字节,每个值应为4字节,因此为144 MB。两者的开销可能相似
Trove使用更少内存的原因是,这些类型专门用于基本值,如int
。它们将直接存储int
值,因此使用4个字节来存储每个值
Java集合HashMap
由许多对象组成。大致如下:有Entry
对象分别指向一个键和一个值对象。由于Java中处理泛型的方式,这些对象必须是对象。在您的情况下,键将是一个整数
对象,它使用16个字节(4个字节标记,4个字节类型,4个字节实际int
值,4个字节填充)AFAIK。这些都是32位系统估计值。因此,HashMap
中的单个条目可能需要16(条目)+16(整数键)+32(但LinkedList为空)字节的内存,这些都需要考虑垃圾收集
如果您有大量的
Integer
对象,那么所需的内存将是使用int
原语存储所有对象所需内存的4倍。这是在Java中实现的干净OOP原则的成本。为什么要调用两次get
?不要将现有列表放到映射中。使用探查器,您将不必猜测什么是慢的,什么是快的…从while循环中删除System.out.println,然后重试。映射中有超过110万个LinkedList。你的记忆力是否已经耗尽?我对这个问题投了否决票,因为它不能用所提供的信息客观地回答,所有答案中的猜测都证明了这一点。为了有效地查明Java性能问题,使用探查器(或类似于线程转储之类的评测)是目前为止最好的方法。但是程序一开始非常快,过一段时间就会慢下来。我试图找出System.out.println()的问题@AKJ有时测量程序的行为会改变程序的特性。通过测量,我指的是System.out.println()。使用剖析器;就是为了这个。众所周知,大量使用System.out.println
会对性能产生重大影响。标准输出不利于调试性能问题。请参阅我关于如何使用GNU Trove原语集合将内存使用量减少5-10倍的回复。粗略估计,HashMap
至少需要3*16字节pe
snsMap.put(key, list);