熟悉 java 内存机制的同学们来帮我瞅瞅我的内存问题吧。 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
buptlee
V2EX    程序员

熟悉 java 内存机制的同学们来帮我瞅瞅我的内存问题吧。

  •  
  •   buptlee
    Xiaoxin2009 2015-01-12 17:07:14 +08:00 4111 次点击
    这是一个创建于 3933 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我的程序要读入一个数据文件,文件的每一行是这样的:
    [1001,0.0,2.846335314101648,0.2385266331350405,0.6889106550987336,0.6265634872937542,0.7916442990777731...]

    每一行是由逗号分隔的11063个字段,我把读到的每一个字段转换成了float类型。每一行存到一个 ArrayList<Float>里面,一共是11063行,也就是说我存下了一个11063*11063的矩阵,矩阵的每个元素是float类型,所以总的大小应该是11063*11063*4/(1024*1024)=466M

    程序读入到1万行的时候就卡住了,查看任务管理器显示内存占用量是3.4G,并且还发生了一个小插曲,当读到4000行的时候卡了20秒,按我的理解可能是在这里数组重新申请了内存,可是有有点疑惑,因为我在程序的最开始是初始化了K_L数组的:
    ArrayList<ArrayList<Float>> K_L = new ArrayList<ArrayList<Float>>(12000);

    按理来说不会发生类似于rehash这种动作的啊。这只是一个小插曲,我的问题是,为什么计算得到的内存是466M,而实际上用了3.4G都没有读完,并且最后停在了10000行处,就算是有额外的开销,也不至于这么大啊。我的程序非常简单,在数据结构和算法上应该是不能更精简了。我想问问同学们关于我的内存问题。还有就是大家在将这种较大的数据读入到内存中时,有什么好的方案吗,比如redis?我不太了解,只是听人提起过。大家分享下吧,thanks。代码如下。

    String each_line = null; while((each_line = K_L_file_handler.readLine())!=null){ String tokens [] = each_line.split(","); ArrayList<Float> K_L_item = new ArrayList<Float>(); for(int i = 0 ; i < tokens.length ; i++){ K_L_item.add( Float.parseFloat(tokens[i])); } K_L.add(K_L_item); } System.out.println("finish initialing the KullbackLeibler divergence"); 
    25 条回复    2015-01-15 14:19:43 +08:00
    kaneg
        1
    kaneg  
       2015-01-12 17:20:53 +08:00   1
    以下是我的一点看法:
    1. 你这里用的是Float对象,并不是float基本类型,基本类型是4个byte,而一个Float对象则要大得多。每个对象都与一个引用,64位jvm,每个应用要8个字节,加上float本身的存储,就已经是3倍了。索尼你不要用Float,直接用float
    2. 其次你使用了ArrayList, 你只初始化了一个维度的长度,另一个维度的没有,那它就需要不断动态调整,这样也有额外的内存消耗。这里你既然已经知道了长度,就应该用数组,不要用ArrayList
    buptlee
        2
    buptlee  
    OP
       2015-01-12 17:27:14 +08:00
    @kaneg 可是ArrayList<float> K_L_item = new ArrayList<float>();
    ArrayList<ArrayList<float>> K_L = new ArrayList<ArrayList<float>>(12000);
    都不被允许啊。
    还有就是数组和ArrayList有本质的区别吗,或者说,什么原因使得ArrayList比数组低效了呢?
    buptlee
        3
    buptlee  
    OP
       2015-01-12 17:30:38 +08:00
    @kaneg 我其实也是想直接用float的,但是在声明的时候,
    ArrayList<float> K_L_item = new ArrayList<float>();会出错,必须要包装类型才可以。
    kaneg
        4
    kaneg  
       2015-01-12 17:32:07 +08:00   1
    基本类型是不能用泛型的,所以ArrayList<float>不支持。
    数组是Java原生提供的数据结构,ArrayList是一个普通的类,不考虑性能的话,ArrayList是很好用,但考虑性能最好老老实实用数组,除非你不在乎内存消耗
    float[][] data = new float[12000][12000];
    songco
        5
    songco  
       2015-01-12 17:33:13 +08:00   1
    我记得java有8 byte的object header, 另外你用Float的话, 还有reference的开销. 还有内存对齐的padding, 我记得java对象是按8 byte对齐的. 这些加起来估计比你算的4 byte多很多.
    具体的情况还是弄个heap dump看看吧.

    这种原始数据如果需要全部加载, 建议用primitive type的多维数组, 或者用c之类的写, 当然如果能根据业务优化一下不要全部加载进来就更好了.
    buptlee
        6
    buptlee  
    OP
       2015-01-12 17:35:07 +08:00
    @kaneg 我重新写一下程序。用惯了ArrayList,对原生态数组表示有点生疏了,thanks,it's so kind of you.
    buptlee
        7
    buptlee  
    OP
       2015-01-12 17:37:15 +08:00
    @songco 嗯,总是被java的内存折磨,看来primitive type数组才是真爱,不该贪图方便一股脑ArrayList。谢谢你。
    mfaner
        8
    mfaner  
       2015-01-12 17:45:52 +08:00   1
    ArrayList<Float> K_L_item = new ArrayList<Float>();
    这里也要指定capacity,觉得应该会影响很大
    ericson
        9
    ericson  
       2015-01-12 18:17:42 +08:00
    也有支持primitve type的高性能集合库:

    * [fastutil](http://fastutil.di.unimi.it/)
    * [OpenHFT](https://github.com/OpenHFT/Koloboke)
    * [hppc](http://labs.carrotsearch.com/hppc.html)
    * [trove](http://trove.starlight-systems.com/)
    songco
        10
    songco  
       2015-01-12 18:18:20 +08:00 via iPhone   1
    @mfaner 增长策略是每次增长一半,然后拷贝……
    msg7086
        11
    msg7086  
       2015-01-12 20:53:56 +08:00 via iPhone   1
    典型的内存密集型操作,托管语言的短板之一啊。
    用C系应该会好得多。
    mfaner
        12
    mfaner  
       2015-01-12 22:20:19 +08:00   1
    再来补充下,split方法是正则匹配,而且内部也是一个无参构造的ArrayList。
    coolcfan
        13
    coolcfan  
       2015-01-12 23:05:17 +08:00   1
    token那块老老实实indexof或者找个高效的库处理吧。。。
    icespace
        14
    icespace  
       2015-01-13 09:32:01 +08:00   1
    有两个思路值得考虑
    1.使用内存型数据库管理数据
    2.使用内存映射文件
    thinkmore
        15
    thinkmore  
       2015-01-13 09:34:09 +08:00   1
    数据量过大了,就算你使用基本类型数组提高也不大,当然是肯定有提升的,毕竟ArrayList内部就是使用的基本类型数组罢了,建议你可以一次性读1W行(不一定是1W,找一个比较合理的数据)
    buptlee
        16
    buptlee  
    OP
       2015-01-13 11:16:23 +08:00
    @icespace redis算是您说的第一个条中的一种吗?
    buptlee
        17
    buptlee  
    OP
       2015-01-13 11:17:29 +08:00
    @thinkmore 不可以呢,我需要全局操作,不能只读一部分数据,业务使然。thanks very much!
    buptlee
        18
    buptlee  
    OP
       2015-01-13 11:17:58 +08:00
    @icespace 内存映射文件是指什么呢,能不能具体点?
    buptlee
        19
    buptlee  
    OP
       2015-01-13 11:22:25 +08:00
    @msg7086 确实是这样。就算是我用了数组去做,还是要消耗接近3G的内存,不过好歹能把所有数据读进去了。
    buptlee
        20
    buptlee  
    OP
       2015-01-13 11:23:08 +08:00
    @coolcfan 比如什么高效的库呢?学习一下。
    wudikua
        21
    wudikua  
       2015-01-13 14:08:15 +08:00
    你想想为什么array list可以不用管数组的下标限制直接可以无限的add而不抛出out of bound就知道了
    icespace
        22
    icespace  
       2015-01-14 10:18:01 +08:00
    @buptlee MappedByteBuffer , 即利用file chanel(NIO)将文件部分或全部映射到内存中,用来处理大文件。这样,你就可以利用操作系统的功能来间接操作文件,而且避免了文件操作错误。需要注意的是,如果处理4G以上的大文件,你需要使用64位JVM来运行。卤煮请移步http://www.linuxidc.com/Linux/2013-11/92895.htm 。所以这个基本上就是你要的。
    icespace
        23
    icespace  
       2015-01-14 10:23:06 +08:00
    @buptlee 另外的一个思路是使用嵌入型数据库,将文件结构化的导入嵌入型数据库后,数据的存储与缓存就交由它来处理,典型的如BerkeleyDB。这个办法比内存映射文件更加有效是因为你的数据是结构化的,这样你就可以充分利用数据的排序、查找、过滤,其处理速度将会远远超过你自己写代码,而且极大减少debug的时间。你不在需要自己组织庞大的数组结构,也不在需要自己去hash。我想唯一的缺点可能就是程序包会稍微大一点。卤煮,我给你的这两条建议,你还满意么?
    buptlee
        24
    buptlee  
    OP
       2015-01-14 21:58:41 +08:00
    满意到哭啊,多谢大神。
    目前在钻研第一个思路。
    第二个思路感觉有点复杂,先不尝试了。
    thanks so much
    icespace
        25
    icespace  
       2015-01-15 14:19:43 +08:00
    @buptlee 我建议你使用第二个思路,第一个虽然解决了文件加载的问题,但是你仍然要在内存中构建巨大的数据结构,实际上并未解决问题的根源。我建议你用第二个,嵌入型数据库不用安装配置,使用同一个jvm,因此相当简单,而且数据库的使用应该是最常用的场景。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2735 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 12:51 PVG 20:51 LAX 05:51 JFK 08:51
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86