写了段小程序,读一个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的方法不对?
求解脱……

解决方案 »

  1.   

    HashMap<Integer, LinkedList<Integer>> inlinkMap //映射关系为 id->follower ids
    HashMap<Integer, LinkedList<Integer>> outlinkMap //映射关系为 id->followee ids占内存,每一个ID对应一个新的链表对象...链表的对象对而大。
    比如:1对应234567890
         2对应134567890里面的34567890都是重复存储...占用多了实际文件的一倍..
      

  2.   

    HashMap<Integer, LinkedList<Integer>> inlinkMap //映射关系为 id->follower ids
    HashMap<Integer, LinkedList<Integer>> outlinkMap //映射关系为 id->followee ids占内存,每一个ID对应一个新的链表对象...链表的对象对而大。
    比如:1对应234567890
         2对应134567890里面的34567890都是重复存储...占用多了实际文件的一倍..
      

  3.   


    还行吧,才300M而已,只针对这个情况来说,我只想知道为什么300M的文件加载到内存要占8G,内存吃在什么地方?
      

  4.   

    试了下
    -Xms20m -Xmx40m  40M空间,只够存储151W左右的数据。
    public class Test {
    public static void main(String[] args) {
    List<Integer> list = new ArrayList<Integer>();
    int i = 0;
    try {
    for (i = 0; i < Integer.MAX_VALUE; i++) {
    list.add(i);
    }
    } finally {
    System.out.println(i);
    }
    }
    }
      

  5.   

    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暂时只能计算到这么多,最好再检查一下程序,是不是多创建了对象。
      

  6.   


    上面的List用的ArrayList
    如果为LinkedList只能存储1.6W左右的数据。
    100/1.6 * 40 = 2500M * 2 + 50M = 5G请先将LinkedList换为ArrayList
      

  7.   


    哥们你太让我感动了,比我还耐心测……我也好好试试去! 但仍期待大牛能一语道破机关~我看了下数据集,对于Map<Integer,List<Integer>> List中平均有22.9条数据。你说的是LinkedList比较占内存哈,恩……
      

  8.   


    其实我之所以用LinkedList而不用ArrayList,是认为LinkedList适合插入、遍历操作,而ArrayList适合遍历、检索操作。我再试试~
      

  9.   

    if(!set.contains(followerId))
                    {
                        set.add(followerId);
                    }
                    if(!set.contains(followeeId))
                    {
                        set.add(followeeId);
                    }
    这个 比较是多余的。set他自己也会比较一次。
      

  10.   

    稍微修改了下。不知道有没有帮助。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();
    }
    }
    }
      

  11.   


    不是从中间插入删除的话,用arrayList快一些
      

  12.   


        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%左右)
      

  13.   

    PS:java中hashMap为了提升性能,在数据快要达到该Map的容量时,会进行扩充。
    比如map容量为100,加载因子为0.75  当数据达到76条时,Map的容量会扩充为200。如果仅有76条数据,那么剩余的空间白白的消耗内存。
    ArrayList,HashSet也是一样。合理的初始化map长度,也有助于提升性能以及节约内存。
      

  14.   

    对对对,当时脑子糊涂了
    你下面的代码我也学习了,String的split函数~
      

  15.   


    恩,刚试了试,用
    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)
      

  16.   

    1.非常大的数据量,通过不建议放入内存中,不然很容易出现内存泄漏.2.文件流没有正常的关闭。3.set内部可以去除重复的,所以在加入之前没必要进行判断。4.去熟悉下ArrayList和LinkedList的区别才能正确的使用。5.整体的代码质量太差,多多学习下代码优化了重构的相关知识。
      

  17.   

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

    3,6
    3,7
    3,16为嘛不做成ID3 List6,7,16
      

  18.   

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

  19.   

    为什么要用LinkedList?换成别的。这个链表结构。插入删除速度快。
      

  20.   


    nice~ 挖到不错的技术网站
      

  21.   

    可以做以下两种优化:
    (1)这两列数据的长度已知,读入内存后可以用一个二维数组(或者两个一维数组)来保存吧,这样效率会高点。
    (2)可以边读,边操作,即一段一段的读,用LineNumberReader,用(),reset()等打个记号,这样用很小的内存就可以搞定。