【问题标题】:How can I reclaim memory from perl?如何从 perl 中回收内存?
【发布时间】:2011-12-08 02:49:22
【问题描述】:

我可以通过哪种方式从 Perl 脚本中回收内存和/或防止 perl 进行内存管理池?

【问题讨论】:

  • 我很想说“杀死它”和/或“一开始就不要运行它”:) “内存管理池”是什么意思?您的主机操作系统也可能会影响此处的响应。

标签: perl memory-management


【解决方案1】:

在一个并行问题上回答:In general, you cannot expect perl to release memory to the OS 在脚本完成/终止之前。终止后,所有分配的内存都会返回给操作系统,但这是操作系统的功能,不是特定于 Perl 的。

因此,如果您有一个长时间运行的脚本,那么您的选项数量是有限的:

  • 您将内存密集型部分委托给子进程。这样,当每个部分完成时,内存将被释放。付出的代价是 IPC 通信。
  • 您使用自己的内存管理结构,通常基于Tie。如果您的结构不是一个简单的结构(即使是标准的基于 NDBM 的哈希也很简单,但功能却非常强大),则要付出的代价是处理与后台存储之间的加载/存储。
  • 您将内存用作宝贵的资源,optimize its usage(通过使用更小的结构、启用内存重用等)。

【讨论】:

    【解决方案2】:

    最有效的方法是拥有大量的虚拟内存,这样perl已经分配但不经常使用的内存就会被分页。

    除此之外,要阻止 perl 只是随着时间的推移分配更多的内存是非常困难的……不是因为它被泄露了,而是因为 perl 真的很喜欢保留分配的东西,以防它们再次被使用。具有相当一致的字符串大小的小型代码库会在一段时间后达到顶峰,但这是一个例外情况。

    在 apache 下,历史上的技术是在进程达到一定大小或在一定数量的请求后终止进程。这不适用于线程 MPM...

    【讨论】:

      【解决方案3】:

      经常取消定义,深度优先。

      【讨论】:

      • 深度优先不应该有任何区别,除非你有其他对深层事物的引用。
      【解决方案4】:

      如果操作系统愿意收回内存,Perl “支持”将内存返还给操作系统。我使用引号是因为,IIRC,Perl 不承诺它什么时候会归还该内存。

      Perl 目前确实承诺什么时候会运行析构函数,什么时候会释放对象(尤其是会以什么顺序发生)。但是释放的内存会进入一个池供 Perl 稍后使用,如果操作系统支持,该内存最终会释放给操作系统。

      【讨论】:

      • 这似乎与我的经验一致——再多的 undefing 或容器对象的破坏都不会减少程序的内存占用。
      【解决方案5】:

      我在从服务器读取大型 SOAP 消息时遇到了类似的问题。据我所知,SOAP::Lite 无法流式传输数据,因此它必须先将其全部加载到内存中,然后我才能处理它。

      即使我只需要一个小列表作为结果,它也会导致程序的内存占用以千兆字节为单位。这是一个问题,因为脚本会进行大量的网络通信,导致内存长时间保持分配状态。

      如前所述,唯一真正的解决方案是 a) 重新设计一切,或 b) fork/exec。我在这里有一个 fork/exec 的例子,应该可以用来说明解决方案:

      # 
      # in general, perl will not return allocated memory back to the OS.
      #
      # to get around this, we must fork/exec
      
      sub _m($){
          my $ln = shift;
          my $s = qx/ ps -o rss,vsz $$ | grep -v RSS /;
          chomp($s);
          print STDERR "$$: $s $ln>\n";
      }
      
      sub alloc_dealloc(){
          # perldoc perlipc for more interesting
          # ways of doing this fork:
          defined(my $pid = open(KID,'-|')) || die "can't fork $!";
      
          my $result = -1;
          if($pid){
              my $s = <KID>; eval $s;
          }else{
              _m(__LINE__);
              my $a = [];
      
              # something that allocates a lot of memory...
              for($i=0;$i<1024*1024*16;$i++){
                  push(@$a,int(rand(3)));
              }
              _m(__LINE__);
      
              # something that processes that huge chunk
              # of memory and returns a very small result
              my $r=0;
              for(@$a){ $r+=$_; }
      
              _m(__LINE__);
              @$a = ();
              _m(__LINE__);
              undef($a);
              _m(__LINE__);
      
              # STDOUT goes to parent process
              print('$result = '.$r.";\n");
              exit;
          }
      
          return $result;
      }
      
      while(1){ 
          _m(__LINE__);
          my $r = alloc_dealloc(); 
          print "Result: $r\n";
          _m(__LINE__);
      }
      

      这将永远运行,产生如下输出:

      9515:  1892  17876 54>
      9519:   824  17876 24>
      9519: 790004 807040 31> # <-- chunk of memory allocated in child
      9519: 790016 807040 38>
      9519: 790068 807040 41> 
      9519: 527924 544892 43> # <-- partially free()d, but mostly not
      9515:  1976  17876 57> # <-- parent process retains its small footprint
      Result: 16783001
      

      【讨论】:

        猜你喜欢
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-04
        • 1970-01-01
        • 2015-11-02
        • 1970-01-01
        相关资源
        最近更新 更多