perlembed/perlcall示例无限内存增长-valgrind说不可能出现泄漏

perlembed/perlcall示例无限内存增长-valgrind说不可能出现泄漏,c,perl,xs,C,Perl,Xs,全部。我的头撞到这个已经有一段时间了。。。我试着把一个基本上是perlembed+perlcall的例子放在一起,或多或少地从evpsgi“借用”了它。问题是,每1000次迭代,它的大小就会增加大约1MB。当运行在一个长寿命的进程中时,这并不是最好的情况(这是我正在处理的用例) 正如标题所述,如果我与valgrind一起运行,它会报告没有可能的泄漏。我使用--trace malloc=yes运行,似乎只有在大量调用结束时才调用free。我知道这可能是perl的任务,但如果它至少能重新使用内存,并

全部。我的头撞到这个已经有一段时间了。。。我试着把一个基本上是perlembed+perlcall的例子放在一起,或多或少地从evpsgi“借用”了它。问题是,每1000次迭代,它的大小就会增加大约1MB。当运行在一个长寿命的进程中时,这并不是最好的情况(这是我正在处理的用例)

正如标题所述,如果我与valgrind一起运行,它会报告没有可能的泄漏。我使用--trace malloc=yes运行,似乎只有在大量调用结束时才调用free。我知道这可能是perl的任务,但如果它至少能重新使用内存,并且在操作系统停止进程之前不会增长,那就太好了

sv_2mortal的条目提到缓冲区可能被“窃取”,但我在代码中添加了对sv_2mortal的调用,但没有任何更改

不用多说,下面是代码。请原谅它的愚蠢。提前谢谢

/*
 *
 * cc `perl -MExtUtils::Embed -e ccopts -e ldopts` -Wall -ggdb test_perl_2.c -o test_perl_2
 *
 * # test.psgi
 * use strict;
 * use warnings;
 * my $app = sub  {
 *     return [ 200, [ test => 1 ], [ sprintf( "%d: Hello!!! from %s\n", $$, __FILE__ ) ] ];
 * };
 *
 */

#include <stdio.h>
#include <EXTERN.h>               /* from the Perl distribution     */
#include <perl.h>                 /* from the Perl distribution     */

static PerlInterpreter *perlinterp;  /***    The Perl interpreter    ***/
static SV *app;

void do_stuff( void );
SV * get_stuff( void );
SV * call_stuff( SV * );

EXTERN_C void xs_init( pTHX );
EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
EXTERN_C void xs_init( pTHX ) {
    char *file = __FILE__;
    dXSUB_SYS;
    /* DynaLoader is a special case */
    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
}

int main( int argc, char **argv, char **env ) {
    char code[ 1024 ];
    char psgi[] = "test.psgi";
    char *embedding[] = { "", "-e", "0" };

    PERL_SYS_INIT3( &argc, &argv, &env );
    perlinterp = perl_alloc();
    PERL_SET_CONTEXT( perlinterp );
    perl_construct( perlinterp );
    perl_parse( perlinterp, xs_init, 3, embedding, (char **)NULL );
    PL_exit_flags |= PERL_EXIT_DESTRUCT_END;

    sprintf( code, "do '%s' or die $@", psgi );
    app = eval_pv( code, TRUE ); /* croak_on_error */

    do_stuff();

    PL_perl_destruct_level = 1;
    perl_destruct( perlinterp );
    perl_free( perlinterp );
    PERL_SYS_TERM();
    return 0;
}

void do_stuff( void ) {
    int body_lastindex, i, count;
    AV *response_av, *body_av;
    SV *stuff_sv, *response_sv, *status, *tmp_body_sv, *body_sv;

//  count = 10000;
    count = 10;
    while( count-- ) {

        ENTER;
        SAVETMPS;

        stuff_sv = get_stuff();
        response_sv = call_stuff( stuff_sv );

        if(
            NULL == response_sv ||
            ! SvROK( response_sv ) ||
            SvTYPE( SvRV( response_sv ) ) != SVt_PVAV
        ) {
            printf( "NULL == response_sv\n" );
            goto CLIENT_END;
        }

        response_av = (AV *)SvRV( response_sv );

        status = *av_fetch( response_av, 0, 0 );
        printf( "status = %ld\n", (long)SvIV( status ) );

        body_av = (AV *)SvRV( *av_fetch( response_av, 2, 0 ) );

        body_sv = newSV( 0 );

        body_lastindex = av_len( body_av );
        for( i = 0; i <= body_lastindex; i++ ) {
            tmp_body_sv = (SV *)*av_fetch( body_av, i, 0 );
            if( SvOK( tmp_body_sv ) ) {
                sv_catsv( body_sv, tmp_body_sv );
            }
        }
        printf( "body_sv = %s\n", SvPV_nolen( body_sv ) );

CLIENT_END:
        FREETMPS;
        LEAVE;
    }
}

SV * get_stuff( void ) {
    HV *stuff_hv;
//    stuff_hv = (HV *)sv_2mortal((SV *)newHV());
    stuff_hv = newHV();

    if( NULL == hv_store( stuff_hv, "SCRIPT_NAME", strlen( "SCRIPT_NAME" ), newSVpv( "", 0 ), 0 ) ) {
        croak( "hv_store( 'SCRIPT_NAME' )" );
    }

    if( NULL == hv_store( stuff_hv, "REQUEST_METHOD", strlen( "REQUEST_METHOD" ), newSVpv( "GET", 3 ), 0 ) ) {
        croak( "hv_store( 'REQUEST_METHOD' )" );
    }

    if( NULL == hv_store( stuff_hv, "REQUEST_URI", strlen( "REQUEST_URI" ), newSVpv( "/abc?def", 8 ), 0 ) ) {
        croak( "hv_store( 'REQUEST_URI' )" );
    }

    if( NULL == hv_store( stuff_hv, "PATH_INFO", strlen( "PATH_INFO" ), newSVpv( "/abc", 4 ), 0 ) ) {
        croak( "hv_store( 'PATH_INFO' )" );
    }

    if( NULL == hv_store( stuff_hv, "QUERY_STRING", strlen( "QUERY_STRING" ), newSVpv( "def", 3 ), 0 ) ) {
        croak( "hv_store( 'QUERY_STRING' )" );
    }

    return newRV_inc( (SV *)stuff_hv );
}

SV * call_stuff( SV *stuff_sv ) {
    SV *response_sv;
    int count;

//  printf( "REQUEST_URI = %s\n", SvPV_nolen( *hv_fetch( (HV *)SvRV( stuff_sv ), "REQUEST_URI", strlen( "REQUEST_URI" ), 0 ) ) );

    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK( SP );
    XPUSHs( stuff_sv ); // stuff_sv is not mortal.
    PUTBACK;
    count = call_sv( app, G_EVAL | G_SCALAR | G_KEEPERR );
    SPAGAIN;
    if( SvTRUE( ERRSV ) ) {
        response_sv = NULL;
        fprintf( stderr, "FATAL: %s", SvPV_nolen( ERRSV ) );
        /* CLEAR_ERRSV() is only available 5.8.9 or later */
        if( SvMAGICAL( ERRSV ) ) {
            mg_free( ERRSV );
            mg_clear( ERRSV );
        }
        sv_setpvn_mg( ERRSV, "", 0 );
        POPs; // causes "warning: value computed is not used"
    }
    else if( count > 0 ) {
        response_sv = POPs; // is this mortal?
        SvREFCNT_inc( response_sv );
    } else {
        response_sv = NULL;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;
    return response_sv;
}
/*
*
*cc`perl-MExtUtils::Embed-eccops-eldopts`-Wall-ggdb test\u perl\u 2.c-o test\u perl\u 2
*
*#test.psgi
*严格使用;
*使用警告;
*我的$app=sub{
*返回[200,[test=>1],[sprintf(“%d:Hello!!!from%s\n”,$$,\uuuuu文件)];
* };
*
*/
#包括
#从Perl发行版中包括/**/
#从Perl发行版中包括/**/
静态PerlInterpreter*PerlInterpreter;/***Perl解释器***/
静态SV*app;
无效的东西(无效);
SV*获取内容(无效);
SV*呼叫(SV*);
外部无效xs初始(pTHX);
外部无效启动动态加载程序(pTHX_uuCv*CV);
外部无效xs初始(pTHX){
char*file=\uuuuu文件\uuuuuu;
dXSUB_系统;
/*DynaLoader是一个特例*/
newXS(“DynaLoader::boot_DynaLoader”,boot_DynaLoader,file);
}
内部主(内部argc、字符**argv、字符**env){
字符码[1024];
char psgi[]=“test.psgi”;
字符*嵌入[]={“,”-e,“0”};
PERL_SYS_INIT3(&argc,&argv,&env);
perlinterp=perl_alloc();
PERL_集_上下文(perlinterp);
perl_构造(perlinterp);
perl_parse(perlinterp,xs_init,3,嵌入,(char**)NULL);
PL_exit_flags |=PERL_exit_DESTRUCT_END;
sprintf(代码“do'%s'或die$@”,psgi);
app=eval_pv(代码,真);/*croak_on_错误*/
做某事;
PL_perl_destruct_level=1;
perl_自毁(perlinterp);
无perl_(perlinterp);
PERL_SYS_TERM();
返回0;
}
void do_stuff(void){
int body_lastindex,i,count;
AV*响应\ AV,*身体\ AV;
SV*人员SV、*响应SV、*状态、*tmp\U身体SV、*身体SV;
//计数=10000;
计数=10;
而(计数--){
进来
SAVETMPS;
stuff_sv=获取stuff();
响应_sv=调用_stuff(stuff_sv);
如果(
NULL==响应值||
!SvROK(响应_sv)||
SvTYPE(SvRV(响应sv))!=SVt\U PVAV
) {
printf(“NULL==response_sv\n”);
转到客户端;
}
响应_av=(av*)SvRV(响应_sv);
状态=*av_-fetch(响应_-av,0,0);
printf(“状态=%ld\n”,(长)SvIV(状态));
body_av=(av*)SvRV(*av_fetch(response_av,2,0));
body_sv=newSV(0);
body_lastindex=av_len(body_av);
对于(i=0;i=0){
response_sv=POPs;//这是凡人吗?
SvREFCNT公司(回应sv);
}否则{
响应_sv=NULL;
}
倒退;
FREETMPS;
离开
返回响应;
}

你没有释放任何东西!你从Perl中得到一个标量,你自己创建了两个,但是没有一个得到释放

泄漏1 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
这有两个问题:

  • 您正在创建引用为2的HV

    newRV_inc
    更改为
    newRV_noinc

  • 你永远不会释放它(或者作为凡人从XS func中返回)

    完成后使用
    SvREFCNT\u dec(stuff\u sv)
    ,可能是在调用
    call\u stuff
    之后

泄漏2 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
同样,没有相应的释放那个标量

SvREFCNT_dec( body_sv );
printf
之后

泄漏3 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
它是否会死其实并不重要。如果它会死,你需要声明它的所有权,因此
inc
是合适的。但是你必须在以后处理完它后释放它

 SvREFCNT_dec( response_sv );

你没有释放任何东西!你从Perl得到一个标量,你自己创建了两个,但是没有一个得到释放

泄漏1 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
这有两个问题:

  • 您正在创建引用为2的HV

    newRV_inc
    更改为
    newRV_noinc

  • 你永远不会释放它(或者作为凡人从XS func中返回)

    完成后使用
    SvREFCNT\u dec(stuff\u sv)
    ,可能是在调用
    call\u stuff
    之后

泄漏2 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
同样,没有相应的释放那个标量

SvREFCNT_dec( body_sv );
printf
之后

泄漏3 你有:

HV *stuff_hv;
stuff_hv = newHV();
return newRV_inc( (SV *)stuff_hv );
body_sv = newSV( 0 );
response_sv = POPs; // is this mortal?
SvREFCNT_inc( response_sv );
它是否会死其实并不重要。如果它会死,你需要声明它的所有权,因此
inc
是合适的。但是你必须在以后处理完它后释放它

 SvREFCNT_dec( response_sv );

Owned!按照您的分步说明操作,现在它的内存使用非常可靠。谢谢!我犯了一个错误。对于泄漏1,您需要对引用进行重新计数,而不是HV。您已经使用
noinc
。Owned!按照您的分步说明操作,现在它的内存使用非常可靠。谢谢!我犯了一个错误。F或泄漏1,您需要dec到参考的refcount,而不是HV。您已经使用
noinc
处理了HV。