2016-11-04 96 views
1

我不得不处理我公司的一个非常老的代码库,它通过perl暴露了C++ apis。Perl XS垃圾回收

在代码评论中,我建议有必要垃圾收集在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

为什么他们认为Perl会尝试垃圾收集由C++分配的内存? *你被要求证明什么? – ysth

+0

@ysth该perl不会垃圾收集由C++分配的内存 – var

回答

3

恐怕谁写(写)人民的Perl XS文档可能认为它太明显,Perl不能神奇地检测到在其他语言(如C++)中进行内存分配来记录t明确的帽子。在perlguts文档页面中有一点说明,通过Perl XS API使用的所有内存必须使用Perl的宏来执行,以帮助您辩论。

+0

“*通过Perl XS API使用的所有内存都必须使用Perl的宏*”,OP的mem不是这样的内存。那段话是指Perl将需要释放的内存。 (例如,传递给'SvPV_set'的缓冲区必须使用Perl的分配器来分配,但是'SvPV_set'不是你通常使用的。)OP的'mem'可以被任何分配器安全地分配。 (您将需要使用相应的释放器以像往常一样释放它。) – ikegami

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

或者你可以采取的XS的功能,如RETVALCLEANUP优势。

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是延迟引用计数递减。如果您在hello返回之前递减由hello创建的SV,则在hello返回之前它将被释放。通过终止它,它将不会被释放,直到调用者有机会检查它或占有它(通过增加它的引用计数)。