50行java程序,内存吃在哪?

大黑山唐僧 2012-05-07 03:21:26
写了段小程序,读一个300M左右的文件,共100万个不同id结点,2200万行。文件内容只有两列,如下:
3,6
3,7
3,16
20,31
20,35
20,36
……

其中3,6表示id为3的用户关注了id为6的用户。我写的程序是把这个文件读入内存,接收的数据结构有三个:
HashSet<Integer> set //结点id的集合
HashMap<Integer, LinkedList<Integer>> inlinkMap //映射关系为 id->follower ids
HashMap<Integer, LinkedList<Integer>> outlinkMap //映射关系为 id->followee ids


代码如下:(关键代码只有中间一小段,前面是变量定义,后面是异常处理)

private void format(String fileName, Set<Integer> set, HashMap< Integer, LinkedList<Integer> > inlinkMap,
HashMap<Integer,LinkedList<Integer> > outlinkMap)
{
System.out.println("Loading...");
FileReader fr = null;
BufferedReader br = null;
LinkedList<Integer> inlinkList = null;
LinkedList<Integer> outlinkList = null;
String strLine = null;
int seperatorIndex, followerId, followeeId;

try {
fr = new FileReader(fileName);
br = new BufferedReader(fr);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
while(true)
{
try
{
strLine = br.readLine();
seperatorIndex = strLine.indexOf(',');
followerId = Integer.parseInt(strLine.substring(0, seperatorIndex));
followeeId = Integer.parseInt(strLine.substring(seperatorIndex+1,strLine.length()));

if(!set.contains(followerId))
{
set.add(followerId);
}
if(!set.contains(followeeId))
{
set.add(followeeId);
}

if(!inlinkMap.keySet().contains(followeeId))
{
inlinkList = new LinkedList<Integer>();
inlinkList.add(followerId);
inlinkMap.put(followeeId, inlinkList);
}
else
{
inlinkList = inlinkMap.get(followeeId);
inlinkList.add(followerId);
}

if(!outlinkMap.keySet().contains(followerId))
{
outlinkList = new LinkedList<Integer>();
outlinkList.add(followeeId);
outlinkMap.put(followerId, outlinkList);
}
else
{
outlinkList = outlinkMap.get(followerId);
outlinkList.add(followeeId);
}

} catch(Exception e)
{
System.out.println("EOF");
try {
br.close();
fr.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
break;
}
}
try {
br.close();
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Loading completed!");

}


运行的时候,我用java -Xms2048m -Xmx8192m XXXXX 命令,分配了8G的内存才能成功,8G以下都提示内存不足。
是不是用HashMap太占内存了?还是我用HashMap的方法不对?
求解脱……
...全文
715 36 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
36 条回复
切换为时间正序
请发表友善的回复…
发表回复
nb70001 2012-05-13
  • 打赏
  • 举报
回复
大数据量不建议一次性都往内存里塞
asmeng123 2012-05-13
  • 打赏
  • 举报
回复
可以做以下两种优化:
(1)这两列数据的长度已知,读入内存后可以用一个二维数组(或者两个一维数组)来保存吧,这样效率会高点。
(2)可以边读,边操作,即一段一段的读,用LineNumberReader,用mark(),reset()等打个记号,这样用很小的内存就可以搞定。
大黑山唐僧 2012-05-12
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 的回复:]

http://www.ibm.com/developerworks/cn/java/j-codetoheap/
去看看吧,测试java几个集合的内存消耗情况。
另外,默认hashMap最少会空闲25%的数据空间出来,
Map map = new HashMap(1000000,1.0f);
[/Quote]

nice~ 挖到不错的技术网站
昨日凡阳 2012-05-11
  • 打赏
  • 举报
回复
为什么要用LinkedList?换成别的。这个链表结构。插入删除速度快。
  • 打赏
  • 举报
回复
http://www.ibm.com/developerworks/cn/java/j-codetoheap/
去看看吧,测试java几个集合的内存消耗情况。
另外,默认hashMap最少会空闲25%的数据空间出来,
Map map = new HashMap(1000000,1.0f);
大黑山唐僧 2012-05-11
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 的回复:]

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。
其他人的回复我没看。但是我觉得。

3,6
3,7
3,16

为嘛不做成ID3 List6,7,16
[/Quote]
亲,我就是这么做的呀,上面那是数据集
看那边 2012-05-10
  • 打赏
  • 举报
回复
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。
其他人的回复我没看。但是我觉得。

3,6
3,7
3,16

为嘛不做成ID3 List6,7,16
大黑山唐僧 2012-05-10
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

1.非常大的数据量,通过不建议放入内存中,不然很容易出现内存泄漏.

2.文件流没有正常的关闭。

3.set内部可以去除重复的,所以在加入之前没必要进行判断。

4.去熟悉下ArrayList和LinkedList的区别才能正确的使用。

5.整体的代码质量太差,多多学习下代码优化了重构的相关知识。
[/Quote]

谢了,对于4、5这样的问题,推荐几本书吧,或是该怎么学起?
zqfddqr 2012-05-09
  • 打赏
  • 举报
回复
2200万行 的有1亿个对象 可定不行了啊 数据库吧
大黑山唐僧 2012-05-09
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 的回复:]

Java code

private static void format02(String fileName, Set<Integer> set, Map<Integer, List<Integer>> inlinkMap,
Map<Integer, List<Integer>> outlinkMap) throws NumberFormatException……
[/Quote]

恩,刚试了试,用
Runtime runtime = Runtime.getRuntime();
runtime.totalMemory() - runtime.freeMemory();
算内存使用量,这方法不知道对不对。
对Map的value数据结构选择 ArrayList LinkedList HashSet,数据量1w个结点,13w条边
结果是:
(ArrayList版) Memory:9010976B(约9M)
(LinkedList版)Memory:12402184B(约12M)
(HashSet版) Memory:16480688B(约16M)
大黑山唐僧 2012-05-09
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 的回复:]

if(!set.contains(followerId))
{
set.add(followerId);
}
if(!set.contains(followeeId))
{
se……
[/Quote]


对对对,当时脑子糊涂了
你下面的代码我也学习了,String的split函数~
卡卡西CC 2012-05-09
  • 打赏
  • 举报
回复
大数据量不建议一次性都往内存里塞
KF无痕 2012-05-09
  • 打赏
  • 举报
回复
1.非常大的数据量,通过不建议放入内存中,不然很容易出现内存泄漏.

2.文件流没有正常的关闭。

3.set内部可以去除重复的,所以在加入之前没必要进行判断。

4.去熟悉下ArrayList和LinkedList的区别才能正确的使用。

5.整体的代码质量太差,多多学习下代码优化了重构的相关知识。
FFF9527 2012-05-08
  • 打赏
  • 举报
回复
PS:java中hashMap为了提升性能,在数据快要达到该Map的容量时,会进行扩充。
比如map容量为100,加载因子为0.75 当数据达到76条时,Map的容量会扩充为200。如果仅有76条数据,那么剩余的空间白白的消耗内存。
ArrayList,HashSet也是一样。合理的初始化map长度,也有助于提升性能以及节约内存。
FFF9527 2012-05-08
  • 打赏
  • 举报
回复

private static void format02(String fileName, Set<Integer> set, Map<Integer, List<Integer>> inlinkMap,
Map<Integer, List<Integer>> outlinkMap) throws NumberFormatException, IOException
{
System.out.println("Loading...");
BufferedReader br = null;
int seperatorIndex, followerId, followeeId;
try
{
br = new BufferedReader(new FileReader(fileName));
String strLine = null;
while ((strLine = br.readLine()) != null)
{
seperatorIndex = strLine.indexOf(',');
followerId = Integer.parseInt(strLine.substring(0, seperatorIndex));
followeeId = Integer.parseInt(strLine.substring(seperatorIndex + 1, strLine.length()));
set.add(followerId);
set.add(followeeId);

if (!inlinkMap.containsKey(followeeId))
{
List<Integer> inlinkList = new ArrayList<Integer>();
inlinkList.add(followerId);
inlinkMap.put(followeeId, inlinkList);
}
else
{
inlinkMap.get(followeeId).add(followerId);;
}

if (!outlinkMap.containsKey(followerId))
{
List<Integer> outlinkList = new ArrayList<Integer>();
outlinkList.add(followeeId);
outlinkMap.put(followerId, outlinkList);
}
else
{
outlinkMap.get(followerId).add(followeeId);
}
}
}
finally
{
if (br != null)
{
br.close();
br = null;
}

}
}

400W数据,200000用户的条件下:
性能提升:23%
内存占用少:36%

主要提升点在:1. linklist变为arraylist(性能和内存的提升)
2. set.add(followerId); (内存减少4%左右)
FFF9527 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 的回复:]

引用 13 楼 的回复:
引用 12 楼 的回复:

40M的Set<Integer>只能存储79W左右的数据,Map<Integer,Integer> 62W数据,Map<Integer,List<Integer>> List中100条数据:能存储6.5W条左右。
计算一下:100Set 50M
100W Map = 100/6.5 * 40 = 615M * 2 = 1.3G +……
[/Quote]

不是从中间插入删除的话,用arrayList快一些
sdojqy1122 2012-05-08
  • 打赏
  • 举报
回复
稍微修改了下。不知道有没有帮助。

public void format(String fileName, Set<Integer> set,
HashMap<Integer, LinkedList<Integer>> inlinkMap,
HashMap<Integer, LinkedList<Integer>> outlinkMap){
System.out.println("Loading...");
FileReader fr = null;
BufferedReader br = null;
LinkedList<Integer> inlinkList = null;
LinkedList<Integer> outlinkList = null;
String strLine = null;
String[] temp = null;
int followerId, followeeId;

try {
fr = new FileReader(fileName);
br = new BufferedReader(fr);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
while ((strLine = br.readLine()) != null) {
temp = strLine.split("[,]");
followerId = Integer.valueOf(temp[0]);
followeeId = Integer.valueOf(temp[1]);
set.add(followerId);
set.add(followeeId);

if (!inlinkMap.keySet().contains(followeeId)){
inlinkList = new LinkedList<Integer>();
inlinkList.add(followerId);
inlinkMap.put(followeeId, inlinkList);
} else {
inlinkMap.get(followeeId).add(followerId);
}

if (!outlinkMap.keySet().contains(followerId)) {
outlinkList = new LinkedList<Integer>();
outlinkList.add(followeeId);
outlinkMap.put(followerId, outlinkList);
} else {
outlinkMap.get(followerId).add(followeeId);
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("Loading completed!");
try {
br.close();
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
sdojqy1122 2012-05-08
  • 打赏
  • 举报
回复
if(!set.contains(followerId))
{
set.add(followerId);
}
if(!set.contains(followeeId))
{
set.add(followeeId);
}
这个 比较是多余的。set他自己也会比较一次。
大黑山唐僧 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]
引用 12 楼 的回复:

40M的Set<Integer>只能存储79W左右的数据,Map<Integer,Integer> 62W数据,Map<Integer,List<Integer>> List中100条数据:能存储6.5W条左右。
计算一下:100Set 50M
100W Map = 100/6.5 * 40 = 615M * 2 = 1.3G + 50M

暂时只能计算到这……
[/Quote]

其实我之所以用LinkedList而不用ArrayList,是认为LinkedList适合插入、遍历操作,而ArrayList适合遍历、检索操作。我再试试~
大黑山唐僧 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]
引用 12 楼 的回复:

40M的Set<Integer>只能存储79W左右的数据,Map<Integer,Integer> 62W数据,Map<Integer,List<Integer>> List中100条数据:能存储6.5W条左右。
计算一下:100Set 50M
100W Map = 100/6.5 * 40 = 615M * 2 = 1.3G + 50M

暂时只能计算到这……
[/Quote]

哥们你太让我感动了,比我还耐心测……我也好好试试去! 但仍期待大牛能一语道破机关~

我看了下数据集,对于Map<Integer,List<Integer>> List中平均有22.9条数据。

你说的是LinkedList比较占内存哈,恩……
加载更多回复(12)

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧