Perl XS垃圾收集

时间:2016-11-04 03:18:34

标签: perl garbage-collection perl-xs

我不得不在我的公司处理一个非常古老的代码库,它通过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);

2 个答案:

答案 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);
    }

或者您可以利用RETVALCLEANUP等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返回之前取消分配。通过对它进行致命,它不会被解除分配,直到调用者有机会检查它或占有它(通过增加其引用计数)。