本帖最后由 yifulou 于 2013-01-31 08:09:05 编辑

解决方案 »

  1.   

    刚看了一个文章,或许可以通过这里开始入手分析:
    http://blog.csdn.net/21aspnet/article/details/7404059
      

  2.   

    1、错误信息显示: 在 16M 限定的内存中无法申请一块 4M 的空间(注意是连续内存空间)
    2、出错前 memory_get_usage 显示已用内存为 13.25M。所以可用的内存只有 16-13.25=2.75
      显然是不够了
    3、fgets($fp, 4096 * 1024); 表示你打算读取 4M 的数据,当然 fgets 函数要先去申请这么大的内存来保存读到的数据
    注意:这块内存是在 fgets 函数结束时就释放的,当然在释放前要把读到的数据抄写到php的变量中去
    换句话说,如果你申请到了 4M 内存,并且也读到了 4M 的数据,那么在抄写变量时还需要有 4M 的内存给php变量
    4、php的函数(内置、自定义)使用同样的方式使用内存:函数结束时释放函数内除静态变量以外的所有局部变量
      

  3.   


    其实您说的我都能看到,但如果您也照我给的例子测试一次的话,您会发现,string(10) "13,893,632"出现了很多次,即按您说的,早就用了13.25M了,但只是在最后一次提示了exhausted,为什么之前没有提示呢?
    其次,memory_get_usage()是emalloc的内存,即是实际变量使用的内存吧?但memory_get_usage(true)-memory_get_usage()的内存是什么呢?我看过一些解释,说memory_get_usage(true)包括内存管理器的内存,可难道内存管理器管理一个1M多的数组需要10M以上,即10倍左右的内存?
    而且,我也尝试过在数组超过1000个元素以后,数组$arr=array()并重新push,那时memory_get_usage(true)是9M多,但我发现,之后,memory_get_usage(true)也并没有被减小,貌似表示,memory_get_usage(true)-memory_get_usage()和管理数组$arr的内存也关系不大,这是为什么呢?现在我怀疑问题发生在内存管理器上,我编译php时候,没有设置内存管理器,用的应该是默认的内存管理器,但我看文档,貌似现在可以编译为别的更好的内存管理器,我正考虑往这方面试试。您认为呢?
      

  4.   


    也有可能是9M多没释放被当做缓存了,也许算是php的优化机制也说不定,不知道也还是对巨大的memory_get_usage(true)-memory_get_usage()内容很困惑,怎么就耗了这么大呢
      

  5.   

    自己再把新的想法在这记录一下,以后可以通过研究过php源码再验证一下,目前有这种猜测:
    1、假设memory_get_usage(true)-memory_get_usage()主要就是数组的管理内存。
    2、每次memory_get_usage(true)增加和fgets请求的大小一致,或许是因为fgets确实需要分配内存,但使用后不释放,当做下一次fgets的缓存和数组的管理内存,直接使用,或许是避免每次都重新分配内存的一种优化方式吧。当这块内存被占满后,再进行下一次分配,所以之前的很多次提示memory_get_usage(true)已经是13.25M,但fgets时却没有提示,由于他也依然在用之前分配而未释放的这段内存区域。
    3、即使$arr=null也不减小或许也是同理,虽然不减小,但也不会像之前一样继续增加内存分配,目的也是避免多次重新分配内存。4、由于管理内存和fgets缓存都在用每次预分配的4M,可能会造成缓存和数组管理内存之间产生一定的内存碎片,间接造成memory_get_usage(true)过分的大。对于这种每次都彻底读出一个小文件全部内容的程序,应该用file函数可以得到很大优化这就验证一下4~~
      

  6.   

    4验证成功~~代码如下:
    $arr = array();
    while(true){
            $events = null;
            flush();
            gc_collect_cycles();
            var_dump(number_format(memory_get_usage(true)));
            $events = file("event.txt");
            var_dump(number_format(memory_get_usage()));
            $arr = array_merge($arr, $events);
    }执行效果如下:



    string(9) "4,528,496"
    string(9) "5,767,168"
    string(9) "4,530,356"
    string(9) "5,767,168"
    string(9) "4,532,088"
    string(9) "5,767,168"
    string(9) "4,533,880"
    string(9) "5,767,168"
    string(9) "4,535,712"
    string(9) "5,767,168"
    string(9) "4,537,492"
    ^C最终我ctrl+c停止了,可以看到,memory_get_usage(true)与memory_get_usage()相差很小,确实有很大的优化效果,也验证了,管理内存绝对不会需要10倍多那么离谱之前的memory_get_usage(true)超大很有可能是内存碎片造成。暂时就当个想法吧,以后遇到这种问题先这么解决了~~回头有时间研究一下内存管理器的源码验证一下~~这个结论确实是瞎猜的算是,希望各位有啥新的想法随时来此赐教~~谢谢~~