Perl XS 垃圾回收
Perl XS garbage collection
我不得不处理我公司中一个非常古老的代码库,它通过 perl 公开了 C++ api。
在代码审查中,我建议有必要对在 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);
恐怕编写(和编写)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);
这三个都完全可以接受。
关于凡人的笔记。
Mortalizing 是一种延迟的引用计数递减。如果您要在 hello
returns 之前递减由 hello
创建的 SV
,它将在 hello
returns 之前被释放。取而代之的是,它不会被释放,直到调用者有机会检查它或占有它(通过增加它的引用计数)。
我不得不处理我公司中一个非常古老的代码库,它通过 perl 公开了 C++ api。
在代码审查中,我建议有必要对在 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);
恐怕编写(和编写)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);
这三个都完全可以接受。
关于凡人的笔记。
Mortalizing 是一种延迟的引用计数递减。如果您要在 hello
returns 之前递减由 hello
创建的 SV
,它将在 hello
returns 之前被释放。取而代之的是,它不会被释放,直到调用者有机会检查它或占有它(通过增加它的引用计数)。