Могу ли я использовать модуль, а затем выгрузить его, чтобы уменьшить его размер?

Отказ от ответственности Я не уверен, что я использую правильные термины. Это не может быть optree, ответственный за раздувание, упомянутое ниже: это могут быть символы, загруженные 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);

Команда Shell

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

Вы можете видеть или не видеть увеличение размера 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

против

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.so а также Fnctl.so - Я определил это с lsof, Это само по себе несколько относительно. Я думаю, что было бы разумно выделить ручку на пакете вызываемого. XSLoader также скрывает, что вы можете освободить этот дескриптор файла - функция доступна в DynaLoader,

Кроме того, кажется, что в libc / dlfcn.h я имею

dlclose()

Функция dlclose() уменьшает счетчик ссылок на динамически загружаемый общий объект, на который ссылается дескриптор. Если счетчик ссылок падает до нуля, то объект выгружается. Все общие объекты, которые были автоматически загружены при вызове dlopen() для объекта, на который ссылается дескриптор, рекурсивно закрываются аналогичным образом.

Успешный возврат из dlclose() не гарантирует, что символы, связанные с дескриптором, будут удалены из адресного пространства вызывающей стороны. В дополнение к ссылкам, полученным в результате явных вызовов dlopen(), общий объект может быть неявно загружен (и счетчик ссылок) из-за зависимостей в других общих объектах. Только после освобождения всех ссылок общий объект может быть удален из адресного пространства.

Я предполагаю, что это может быть подозрительно, DynaLoader::dl_unload_file звонит dlclose и это похоже на работу.

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

После того, как я уничтожил все файлы, загруженные DynaLoader а также XSLoader делая вышеупомянутый RSS все еще не понижался.

2 ответа

Да, ты можешь.

Но есть драконы, и практически нет.

SV и OP расположены на аренах. ОП держат указатели на свои данные, СВ. Эти OP и SV могут быть освобождены через undef, при этом части, размещенные в malloc, немедленно освобождаются, а арены (~70 OP) освобождаются, когда все OP в них освобождаются.

Тогда у вас есть глобальные переменные, которые можно легко освободить, пройдя пространство имен. Но остерегайтесь не уничтожать данные, ссылки на которые где-то еще существуют, и их обработчик DESTROY не может справиться с этим. Там много небезопасного кода DESTROY, потому что никто этого не делает.

И иногда на удаленные глобальные ссылки ссылаются откуда-то еще, поэтому они не будут освобождены, просто отсчет ссылок сбрасывается.

И тогда у вас есть внешний код XS, для которого вы должны позвонить dl_unload_file(),

В твоем случае use POSIX создает тонны импорта в main:: namespace, псевдонимы GV всех импортируемых функций. Их также необходимо удалить.use POSIX (); импорт будет пропущен, поэтому потребуется НАМНОГО меньше памяти, и есть вероятность, что он может быть полностью удален.

Чтобы увидеть то, что на самом деле не undef'd увидеть

#!/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, а путем удаления записи stash.
  3. Не импортируйте POSIX и такие старые импортирующие модули, а используйте полное имя. Очистить это сложно.
  4. Вы не можете освободить SV только OP, память будет увеличиваться, а не уменьшаться.

Арены головы и тела SV никогда не освобождаются, они просто используются повторно. Таким образом, вы можете только уменьшить optree, а не данные.

SV за символом просто установлены в TEMP, если undef'd, поэтому его память никогда не освобождается, а сам символ (GV) очищается только с undef. ОП удаляются путем отмены определения резюме, но система malloc редко освобождает его, только если освобождается полная страница и с помощью glibc с вызовом malloc_trim(0)и память Perl слишком разбросана. Это все еще связанный список с небольшим уплотнением в конце концов.

Rss немного снижается от разгрузки XS к освобожденному, но все же выше, чем после первоначального импорта.

Мой наблюдатель watch -n1 'ps -o "comm,rss,vsize" |grep perl;' потому что это работает также на BSD/ Дарвин.

Я написал Internals::gc() для cperl фактически обходить все арены и освобождать пустые, но это довольно нестабильно и не рекомендуется, так как виртуальная машина может "правильно" обращаться с этими свободными SV только во время глобального уничтожения, но не во время выполнения. См. https://github.com/perl11/cperl/issues/336

Вообщем нет. Мелкие детали в том, что почти никто не сжимает свою собственную память, потому что почти все используют библиотеку C malloc (и друзья) призывают распределять память, прямо или косвенно. И нет (стандартного) способа заставить библиотеку C освободить память (отправить ее обратно в ОС). Perl не отличается здесь - один раз mallocЭд и freed, библиотека C, от которой зависит Perl, сохраняет память для будущего использования, поэтому, если вам нужно повторно использовать память, не требуются дорогостоящие вызовы ядра (в частности, brk), и его можно просто использовать повторно. Фактически, именно этим и занимаются ваши сценарии выгрузки - когда вы вернетесь и повторно используете следующие 2 МБ в оставшейся части серверного процесса, вы будете повторно использовать память, а не вызывать brkи ты будешь намного быстрее

Это можно сделать, если вы берете на себя владение выделением памяти и вызываете brk себя, но это редко стоит того. И получение perl для использования этого распределителя потребует некоторых изменений кода для perl и перекомпиляции. Наверное, не то, что вы хотите сделать.

Другие варианты: либо кусать пули, загружать POSIX до разветвления любых серверов (что должно оставить все это в общей памяти для копирования при записи, и, следовательно, занимать только 2 МБ памяти для серверов 5k), либо выполнить команду fork, load POSIX в дочернем элементе, сделайте грязную работу, выйдите из дочернего элемента и продолжите в родительском. Мне кажется, это относительно медленно.

Другие вопросы по тегам