我尝试使用文件内容构建地图,我的代码如下:
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
....
我尝试使用两个System.out.println()子句来判断BufferedReader和HashMap的性能而不是Java分析器。 有时在获取行号信息后获取地图大小的信息需要一段时间,有时,在获取地图大小后获取行号信息需要一段时间。我的问题是:哪个让我的节目变慢了?用于大型文件的BufferedReader或用于大型地图的HashMap?
答案 0 :(得分:3)
如果从Eclipse内部进行测试,那么你应该知道写入stdout / stderr会导致巨大的性能损失,因为Eclipse会在Console视图中捕获这个ouptut。即使在Eclipse之外,在紧密循环内打印也始终是一个性能问题。
但是,如果您抱怨的是处理3000万行后经历的减速,那么我敢打赌这是一个内存问题。首先它由于强烈的GC'而减慢,然后它与OutOfMemoryError
打破。
答案 1 :(得分:2)
您必须使用一些分析工具检查您的程序,以了解它为什么慢。 一般情况下,文件访问比内存操作要慢得多(除非你受限于内存和多余的GC),所以猜测读取文件可能会慢一些。
答案 2 :(得分:2)
在您分析之前,您不会知道什么是慢的,什么不是。
最有可能的是,System.out
将显示为瓶颈,然后您将不得不在没有它们的情况下进行分析。 System.out
是您可以为查找性能瓶颈所做的最差事情,因为这样做通常会增加更糟糕的瓶颈。
对代码的一种显着优化是移动行
snsMap.put(key, list);
进入if
语句。您只需在创建新列表时将其放入。否则,put将只替换当前值。
与Integer
对象相关联的Java成本(特别是在Java Collections API中使用Integers)主要是内存(因此垃圾收集!)问题。您有时可以通过使用原始集合(例如 GNU trove )获得显着收益,具体取决于您可以调整代码以便有效地使用它们。 Trove的大部分收益都在于内存使用。绝对尝试重写代码以使用GNU trove中的TIntArrayList
和TIntObjectMap
。我也避免使用链表,尤其是原始类型。
粗略估计,HashMap<Integer, List<Integer>>
每个条目至少需要3 * 16个字节。双向链表再次需要存储每个条目至少2 * 16个字节。 1米键+ 30米值~1 GB。尚未包含任何开销。使用GNU特洛伊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
个对象,那么将占用4倍的内存,就好像你可以使用int
个原语来存储所有内容一样。这是您为Java实现的清洁OOP原则所付出的代价。
答案 3 :(得分:0)
最好的方法是使用分析器运行程序(例如,JProfile)并查看哪些部分很慢。例如,调试输出也会降低程序的速度。
答案 4 :(得分:0)
哈希地图并不慢,但实际上它是地图中最快的。 HashTable是地图中唯一安全的线程,有时可能很慢。
重要提示:在您阅读数据后关闭BufferedReader和File ...这可能有所帮助。
例如:br.close() file.close()
请从任务管理器检查系统进程,可能还有进程在后台运行。
有时eclipse是真正的资源,所以尝试从控制台运行它来检查它。