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

Eva*_*oll 9 perl memory-management perl-module shared-libraries xs

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

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

  1. Rexecing perl
  2. 分叉

我试过的事情,

  1. Class::Unload->unload('POSIX');
  2. Symbol::delete_package('POSIX');
  3. no POSIX;

这是一个简单的测试创建文件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);
Run Code Online (Sandbox Code Playgroud)

Shell命令

perl ./test.pl & watch -n1 'ps -C perl -o "cmd rss";'
Run Code Online (Sandbox Code Playgroud)

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

追踪POSIX.pm我看到它究竟使用XSLoader哪种用途DynaLoader.

做一些快速比较检查/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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

dlclose()

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

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

所以我猜这可能是怀疑,DynaLoader::dl_unload_file正在呼唤dlclose它似乎确实有效.

foreach my $dlref ( @DynaLoader::dl_librefs ) {
  print DynaLoader::dl_unload_file($dlref);
}
Run Code Online (Sandbox Code Playgroud)

在我修复了所有加载的文件之后DynaLoader,XSLoader通过执行上述操作,RSS仍然没有丢失.

Tan*_*lus 5

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

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

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

  • 我编译了`-Uusemymalloc`这是glibc,我已经确认Perl确实从堆中释放回操作系统.它只是没有自由操作码. (6认同)

rur*_*ban 5

是的你可以。

但有龙,而且实际上没有。

SV 和 OP 被分配在竞技场中。OP 保存指向其数据、SV 的指针。这些 OP 和 SV 可以通过 undef 释放,其中 malloc 的部分会立即释放,并且当其中的所有 OP 都被释放时,arena(约 70 个 OP)也会被释放。

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

有时要删除的全局变量是从其他地方引用的,因此它不会被释放,只是引用计数下降。

然后你就有了外部 XS 代码,你必须调用dl_unload_file().

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

看看什么不是真正的 undef'd see

#!/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);
Run Code Online (Sandbox Code Playgroud)

结果,

=>
  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
Run Code Online (Sandbox Code Playgroud)

这表明

  1. 符号不可靠,删除只读、受保护的符号和
  2. 全局符号(在 main:: 中)不是由 undef 释放的,而是通过删除存储条目来释放的。
  3. 不要导入 POSIX 和此类旧的大量导入模块,而应使用全名。清除这些是很难的。
  4. 你不能只释放 SVs OPs,内存大部分会增加,而不是缩小。

SV 头部和身体区域永远不会被释放,它们只是被重复使用。所以只能收缩optree,而不能收缩数据。

如果 undef'd,符号后面的 SV 只是设置为 TEMP,因此它的内存永远不会释放,并且符号本身(GV)只能通过 undef 清除。OP 通过 undef'ing CV 来删除,但系统 malloc 很少释放它,只有在释放整页并且使用 glibc 调用 时才释放它malloc_trim(0),并且 perl 的内存太分散了。毕竟它仍然是一个没有太多压缩的链表。

从卸载 XS 到释放,RSS 略有下降,但仍然高于初始导入后的水平。

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

Internals::gc()为 cperl 编写了一个程序来实际遍历所有竞技场并释放空的竞技场,但它非常不稳定且不推荐,因为虚拟机只能在全局销毁期间“正确”处理这些空闲的 SV,而不是在运行时。请参阅https://github.com/perl11/cperl/issues/336

  • 不可以。可以通过删除 GV 的哈希条目来释放 GV。问题完全在于释放容纳 SV 头部和身体的竞技场。我目前正在研究这个:https://github.com/perl11/cperl/issues/336(请参阅附带的提交“add Internals::gc() WIP”)我现在可以删除所有空头区域,但是还没有尸体。在制品 (2认同)