Сборка мусора Perl XS
Мне приходилось иметь дело с действительно старой кодовой базой в моей компании, в которой API-интерфейс C++ был открыт через perl.
В одном из обзоров кода я предположил, что необходимо собрать мусор в памяти, которая была выделена в C++.
Вот скелет кода:
char* convert_to_utf8(char *src, int length) {
.
.
.
length = get_utf8_length(src);
char *dest = new char[length];
.
.
// No delete
return dest;
}
Perl xs определение:
PROTOTYPE: ENABLE
char * _xs_convert_to_utf8(src, length)
char *src
int length
CODE:
RETVAL = convert_to_utf8(src, length)
OUTPUT:
RETVAL
Итак, у меня был комментарий, что память, созданная в функции C++, не будет собирать мусор Perl. И 2 Java-разработчики думают, что это потерпит крах, поскольку Perl будет собирать мусор, выделенный C++. Я предложил следующий код.
CLEANUP:
delete[] RETVAL
Я здесь не прав?
Я также запустил этот код и показал увеличение использования памяти с разделом CLEANUP и без него. Но они просят точную документацию, которая доказывает это, и я не мог найти это.
Клиент Perl:
use ExtUtils::testlib;
use test;
for (my $i=0; $i<100000000;$i++) {
my $a = test::hello();
}
Код C++:
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include <stdio.h>
char* create_mem() {
char *foo = (char*)malloc(sizeof(char)*150);
return foo;
}
XS код:
MODULE = test PACKAGE = test
char * hello()
CODE:
RETVAL = create_mem();
OUTPUT:
RETVAL
CLEANUP:
free(RETVAL);
2 ответа
Я боюсь, что люди, которые писали (и пишут) документацию по Perl XS, вероятно, считают слишком очевидным, что Perl не может волшебным образом определить распределение памяти, сделанное в других языках (например, C++), чтобы задокументировать это явно. На странице документации perlguts есть кое- что, что говорит, что вся память, которая будет использоваться через Perl XS API, должна использовать макросы Perl, чтобы это могло помочь вам спорить.
Когда вы пишете код XS, вы пишете код C (или иногда C++). Вам все еще нужно написать правильный C/C++, который включает в себя освобождение выделенной памяти при необходимости.
Клеевая функция, которую вы хотите создать в XS:
void hello() {
dSP; // Declare and init SP, the stack pointer used by mXPUSHs.
char* mem = create_mem();
mXPUSHs(newSVpv(mem, 0)); // Create a scalar, mortalize it, and push it on the stack.
free(mem); // Free memory allocated by create_mem().
XSRETURN(1);
}
newSVpv
делает копию mem
вместо того, чтобы завладеть им, так что вышеизложенное ясно показывает, что free(mem)
необходимо освободить mem
,
В XS вы могли бы написать это как
void hello()
CODE:
{ // A block is needed since we're declaring vars.
char* mem = create_mem();
mXPUSHs(newSVpv(mem, 0));
free(mem);
XSRETURN(1);
}
Или вы можете воспользоваться функциями XS, такими как RETVAL
а также CLEANUP
,
SV* hello()
char* mem; // We can get rid of the block by declaring vars here.
CODE:
mem = create_mem();
RETVAL = newSVpv(mem, 0); // Values returned by SV* subs are automatically mortalized.
OUTPUT:
RETVAL
CLEANUP: // Happens after RETVAL has been converted
free(mem); // and the converted value has been pushed onto the stack.
Или вы также можете воспользоваться картой типов, которая определяет, как преобразовать возвращаемое значение в скаляр.
char* hello()
CODE:
RETVAL = create_mem();
OUTPUT:
RETVAL
CLEANUP:
free(RETVAL);
Все три из них вполне приемлемы.
Записка о смертных.
Смертность - отсроченное уменьшение количества ссылок. Если бы вы уменьшить SV
создано hello
до hello
возвращается, он будет освобожден до того, как hello
возвращается. Вместо этого он будет освобожден от ответственности, пока вызывающий абонент не получит возможность осмотреть его или завладеть им (путем увеличения количества ссылок).