我可以使用模块,稍后卸载它会缩小optree吗?

时间:2017-10-13 21:27:09

标签: perl memory-management shared-libraries perl-module xs

免责声明我不确定我是否使用了正确的条款。它可能不是负责下面提到的膨胀的选择:它可能是DynaLoader加载的符号未被释放。

是否可以使用模块,如POSIX.pm,卸载它并减少(缩小或修剪)不带

的光纤
  1. Rexecing perl
  2. 分岔
  3. 我尝试过的事情,

    1. Class::Unload->unload('POSIX');
    2. Symbol::delete_package('POSIX');
    3. no POSIX;
    4. 这是一个简单的测试创建文件test.pl

      $|++;
      use Symbol;
      use Class::Unload;
      use POSIX;
      
      print "GOT POSIX";
      sleep(3);
      
      no POSIX;
      Class::Unload->unload('POSIX');
      Symbol::delete_package('POSIX');
      print "unloaded";
      
      sleep(3);
      

      Shell命令

      perl ./test.pl & watch -n1 'ps -C perl -o "cmd rss";'
      

      您可能会或可能看不到RSS大小增加(POSIX可能会在watch生成ps之前加载)。但是,我希望看到它缩小。

      确切地追踪POSIX.pm我使用XSLoader使用DynaLoader的确切dlclose

      /proc/$$/smaps中进行一些快速比较检查我已经确定使用POSIX.pm会导致表示空间差异的堆分配。堆上的第一个分配在使用POSIX.pm时大大增加:

      56122fe4c000-561230040000 rw-p 00000000 00:00 0                          [heap]
      Size:               2000 kB
      Rss:                1956 kB
      Pss:                1956 kB
      Shared_Clean:          0 kB
      Shared_Dirty:          0 kB
      Private_Clean:         0 kB
      Private_Dirty:      1956 kB
      Referenced:         1956 kB
      Anonymous:          1956 kB
      AnonHugePages:         0 kB
      ShmemPmdMapped:        0 kB
      Shared_Hugetlb:        0 kB
      Private_Hugetlb:       0 kB
      Swap:                  0 kB
      SwapPss:               0 kB
      KernelPageSize:        4 kB
      MMUPageSize:           4 kB
      Locked:                0 kB
      VmFlags: rd wr mr mw me ac sd
      

      VS

      560c9f6ba000-560c9f6fc000 rw-p 00000000 00:00 0                          [heap]
      Size:                264 kB
      Rss:                 220 kB
      Pss:                 220 kB
      Shared_Clean:          0 kB
      Shared_Dirty:          0 kB
      Private_Clean:         0 kB
      Private_Dirty:       220 kB
      Referenced:          220 kB
      Anonymous:           220 kB
      AnonHugePages:         0 kB
      ShmemPmdMapped:        0 kB
      Shared_Hugetlb:        0 kB
      Private_Hugetlb:       0 kB
      Swap:                  0 kB
      SwapPss:               0 kB
      KernelPageSize:        4 kB
      MMUPageSize:           4 kB
      Locked:                0 kB
      VmFlags: rd wr mr mw me ac sd
      

      我已经确认了一些事情,修改命名空间不会将打开的文件句柄丢弃到POSIX.soFnctl.so - 我用lsof确定了这一点。这本身就有点令人担忧。我认为在被调用者的包上分配句柄是有意义的。 XSLoader也模糊了您可以释放该文件句柄 - DynaLoader中提供的功能。

      此外,似乎在libc / dlfcn.h我有

        

      dlclose()

           

      函数dlclose()减少handle引用的动态加载的共享对象的引用计数。如果引用计数降为零,则卸载该对象。在句柄引用的对象上调用dlopen()时自动加载的所有共享对象以相同的方式递归关闭。

           

      从dlclose()成功返回并不能保证从调用者的地址空间中删除与句柄关联的符号。 除了显式dlopen()调用产生的引用之外,由于其他共享对象的依赖性,共享对象可能已被隐式加载(并引用计数)。只有在释放所有引用后,才能从地址空间中删除共享对象。

      所以我猜这可能是可疑的,DynaLoader::dl_unload_file正在调用https://technet.microsoft.com/en-us/library/cc754628(v=ws.10).aspx,它似乎确实有效。

      foreach my $dlref ( @DynaLoader::dl_librefs ) {
        print DynaLoader::dl_unload_file($dlref);
      }
      

      通过执行上述操作后,我使用DynaLoaderXSLoader加载了所有文件后,RSS仍然没有丢失。

2 个答案:

答案 0 :(得分:5)

一般来说,没有。坚韧不拔的细节是几乎没有人缩小自己的记忆,因为几乎每个人都使用C库malloc(和朋友)调用来直接或间接地分配内存。并且没有(标准)方法告诉C库释放内存(将其发送回操作系统)。 Perl在这里没有什么不同 - 一旦mallocfree d,Perl依赖的C库保留了内存以供将来使用,这样如果你需要重用内存,就不需要昂贵的内核调用(具体来说,brk),它可以简单地重复使用。事实上,这就是你的卸载方案正在做的事情 - 当你回来并在服务器进程的其余部分重用下一个2MB时,你将重新使用内存,而不是调用brk,而你'会快得多。

如果您接管内存分配所有权并自己致电brk,则可以这样做,但这很少值得。并且让perl使用该分配器将需要对perl和重新编译进行一些代码更改。可能不是你想做的事。

其他选项是硬着头皮,在分离任何服务器之前加载POSIX(这应该将所有这些都留在共享的写时复制内存中,因此对于5k服务器只占用2MB内存),或者fork,在子节点中加载POSIX,执行脏工作,退出子节点并继续在父节点中。这对我来说似乎相对缓慢。

答案 1 :(得分:4)

是的,你可以。

但是有龙,实际上没有。

SV和OP在竞技场中分配。 OP持有指向他们数据的指针,SV。那些OP和SV可以通过undef释放,malloc的部分立即被释放,并且当其中的所有OP被释放时,竞技场(~70个OP)被释放。

然后你有了可以通过遍历命名空间轻松释放的全局变量。但要注意不要破坏其他地方仍然存在引用的数据,并且它的DESTROY处理程序无法处理。那里有很多不安全的DESTROY代码,因为没有人这样做。

有时被删除的全局变量是从其他地方引用的,所以它不会被释放,只是refcount会丢失。

然后你有外部XS代码,你必须为此调用dl_unload_file()

在您的情况下,use POSIX会在main :: namespace中创建大量导入,导入所有导入函数的GV别名。它们也需要删除。 use POSIX ();将跳过导入,因此需要更少的内存,并且可能会被完全删除。

要查看什么不是真的没有看到

#!/usr/bin/perl
$|++;
my $s = shift // 3;
sub rss { `ps -o "comm,rss,vsize" | grep perl` }
print "BEGIN ",scalar keys %main::," ",rss;
require Symbol;
#require Class::Unload;
require POSIX;

print "GOT POSIX ",scalar keys %main::," ",rss;
sleep($s);

POSIX->import;
print "IMPORT POSIX ",scalar keys %main::," ",rss;
sleep($s);

POSIX->unimport;
#Class::Unload->unload('POSIX');
Symbol::delete_package('POSIX');

for (keys %main::) {
  #print "$_\n";
  undef ${$_} unless /^(STD|!|0|1|2|\]|_)/;
  undef &{$_} unless /rss/;
  undef @{$_};
  # clear the GV
  undef *{$_} unless /^(STD...?|rss|main::|DynaLoader::|_|!)$/;
  # delete the GV
  delete $main::{$_} unless /^(STD...?|rss|main::|DynaLoader::|_|!)$/;
}
#Symbol::delete_package('main::'); # needs a patched Symbol
print "unloaded ",scalar keys %main::," ",rss;

sleep($s);

DynaLoader::dl_unload_file($_) for @DynaLoader::dl_librefs;
undef *DynaLoader::;
print "unload XS ",scalar keys %main::," ",rss;
#print "  $_\n" for keys %main::;
print "POSIX::$_\n" for keys %POSIX::;
print "freed ",scalar keys %main::," ",rss;
sleep($s);

结果,

=>
  BEGIN 45 /usr/src/perl/bl   3192  2451188
  GOT POSIX 70 /usr/src/perl/bl   6112  2468844
  IMPORT POSIX 645 /usr/src/perl/bl   6928  2468844
  unloaded 8 /usr/src/perl/bl   7120  2468844
  unload XS 8 /usr/src/perl/bl   7040  2468596
  freed 8 /usr/src/perl/bl   7048  2468596

显示

  1. 符号不可靠删除只读,受保护的符号和
  2. 全局符号(在main::)中没有被undef释放,而是删除了存储条目。
  3. 请勿导入POSIX和此类旧的重载模块,而应使用全名。清除这些很难。
  4. 你无法释放SV只有OP,内存大多会增加,而不是缩小。
  5. SV头部和身体竞技场永远不会被释放,它们只是被重复使用。因此,您只能缩小optree,而不是数据。

    符号后面的SV只是设置为TEMP(如果未定义),因此它的内存永远不会被释放,符号本身(GV)只能用undef清除。 通过取消CV来删除OP,但系统malloc很少释放它,只有当一个完整页面被释放并且glibc调用malloc_trim(0)时,并且perl的内存太多了。它仍然是一个链接列表,毕竟压缩不多。

    rss从卸载XS有点下降到释放,但仍然高于初始导入后。

    我的观察者是watch -n1 'ps -o "comm,rss,vsize" |grep perl;'因为这也适用于BSD / darwin。

    我为cperl编写了一个Internals::gc()来实际走遍所有竞技场并释放空的竞技场,但它非常不稳定且不推荐,因为VM只能在全局破坏期间“正确”处理那些免费的SV,而不是在运行时。见https://github.com/perl11/cperl/issues/336