我不得不在我的公司处理一个非常古老的代码库,它通过perl暴露了C ++ apis。
在代码审查中,我建议有必要对用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);
答案 0 :(得分:3)
我担心编写(和编写)Perl XS文档的人可能会认为Perl不能神奇地检测其他语言(如C ++)中的内存分配以明确记录。 perlguts文档页面中有一点说要通过Perl XS API使用的所有内存都必须使用Perl的宏来帮助您进行争论。
答案 1 :(得分:1)
编写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);
}
或者您可以利用RETVAL
和CLEANUP
等XS功能。
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.
或者您也可以利用typemap,它定义了如何将返回值转换为标量。
char* hello()
CODE:
RETVAL = create_mem();
OUTPUT:
RETVAL
CLEANUP:
free(RETVAL);
这三个都是完全可以接受的。
关于凡人的说明。
Mortalizing是延迟参考计数减少。如果您在SV
返回之前递减hello
创建的hello
,则会在hello
返回之前取消分配。通过对它进行致命,它不会被解除分配,直到调用者有机会检查它或占有它(通过增加其引用计数)。