关于C#:使用mexMakeMemoryPersistent调用MEX文件时,如何在后续调用中将变量重新分配其指针?

When a MEX file is called using mexMakeMemoryPersistent, how are variables re-assigned their pointers on subsequent calls?

我正在尝试从MATLAB的C MEX API绕过mexMakeMemoryPersistent()

我不明白-当多次使用MEX文件并使用mexMakeMemoryPersistent()时,第二次调用MEX文件时,分配给它的内存如何分配给MEX文件?

例如,假设我有一个名为myFunc

的MEX文件

它包含以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int* myVar = NULL;

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if(myVar == NULL)
    {
        myVar = (int*)mxCalloc(sizeof(int*), 10);
        mexMakeMemoryPersistent(myVar);
        myVar[0] = 1;
    }

    // Do the thing I want with it
    myVar[0] *= 2;
}

我们第一次做

fx>> myFunc()

在MATLAB中,很明显,myVar将是NULL,然后mxCalloc将获取一些内存并获得指向它的指针,依此类推。

但是在返回之后又被再次调用该怎么办:

fx>> myFunc()

我们告诉MATLAB不要破坏我们刚刚为myVar分配的内存。但是,当运行myFunc的新实例时,该指针如何重新分配到myVar中?如果不是,myVar就是NULL,我们回到第一个正方形。它是否维护变量名列表以及分配给它们的内存?但是,如果我们具有奇怪的作用域规则,继承(对于C)等会发生什么?

它如何解决需要将什么变量传递给该指针,并实际进行传递?

我没有麻烦使用它,我只是想从概念上理解它,因为我认为它确实很整洁。


MEX文件在首次调用时就会加载到内存中。此时,MEX文件中的全局变量将在内存中获取一个位置并进行初始化。接下来调用mexFunction,您的代码将有机会分配内存并将指针分配给该全局变量。

下次调用MEX文件时,该文件仍会加载到内存中,并且这些全局变量仍然存在。这次MATLAB只需要调用mexFunction

在MATLAB中执行clear mexclear all时,将从内存中卸载MEX文件。全局变量将在此时停止存在。因为您使用mxCalloc分配内存,所以MATLAB可以回收丢失的内存。如果您改用calloc,则此时可能会泄漏内存。

下次调用MEX文件时,就像第一次调用该文件。

请注意,由于MEX文件是已编译的二进制文件,因此变量名不再可见(调试信息中除外)。机器码只处理内存地址和寄存器。

在C的情况下,作用域规则,继承等都只是导致可以使用C或任何其他编译语言获得的同一机器代码的抽象。

一些情况需要澄清:

1
2
3
4
5
6
7
8
9
10
11
12
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    static int* myVar = NULL; // This is basically the same as a global myVar in this case
    if(myVar == NULL)
    {
        myVar = (int*)mxCalloc(sizeof(int*), 10);
        //mexMakeMemoryPersistent(myVar); // Let's leave out this line!
        myVar[0] = 1;
    }

    myVar[0] *= 2;
}

在上述情况下,我们没有使内存持久化。 myVar指针在MEX文件调用中保留,但不保留所指向的内存。第2次调用MEX文件时,myVar[0] *= 2会执行某些非法操作,并可能导致MATLAB崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    int* myVar = NULL; // This is now a local variable
    if(myVar == NULL)
    {
        myVar = (int*)mxCalloc(sizeof(int*), 10);
        mexMakeMemoryPersistent(myVar);
        myVar[0] = 1;
    }

    myVar[0] *= 2;
}

在上述情况下,每次调用MEX文件时,myVar将为NULL,因此每次将分配新的内存。内存是持久性的,因此最终您将用尽此内存。

1
2
3
4
5
6
7
8
9
10
11
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    static int* myVar = NULL;
    if(myVar == NULL)
    {
        myVar = (int*)malloc(sizeof(int*), 10); // Using the system malloc
        myVar[0] = 1;
    }

    myVar[0] *= 2;
}

在上述情况下,一切正常,但从未释放过由malloc分配的内存。当您执行clear allclear mex时,MEX文件将被清除,myVar静态变量将被删除,但是malloc分配的内存仍然存在。您再次泄漏内存。如果要用这种方法,需要使用mexAtExit()

注册一个当MEX文件存在时要运行的函数。

1
2
3
4
5
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    static int myVar[10] = {1};
    myVar[0] *= 2;
}

在上述情况下,我们使用静态变量保存所有数据,不使用动态内存分配,我们无需担心内存泄漏。除非数组很大,否则我建议这样做。


对于我想清除的mexMakeMemoryPersistent和mexMakeArrayPersistent的工作方式似乎有些困惑。免责声明:以下内容基于我从已经执行的测试中观察到的行为...不一定在官方的MATLAB文档中。

mxArray变量标头有一个字段,我将称之为VariableType,该字段包含一个指示其类型的变量的值。例如,对于R2018b和更早版本:

1
2
3
4
5
6
struct mxArray_header {
    void *RevCrossLink;
    mxClassID ClassID;
    int VariableType;
    :
    etc.

对于工作区中的变量,VariableType将为0(正常)。通常,传递给prhs []的变量将是普通类型。

所有分配内存(mxArray变量或原始内存)的官方API函数的所有(*)将该内存的地址放在mex例程的MATLAB Memory Manager临时分配列表中。注意:mxArray变量(即,mxGetPr()和朋友后面的东西)的"数据"存储器不在此分配列表中……其配置完全取决于它所属于的mxArray。使用官方API函数创建的mxArrays的VariableType是4(临时)。

(*)mxArrayToString()曾经是一个例外,但在R2017a中已得到修复。

当mex例程退出时,据我所知,发生了以下事情:

  • plhs []变量的共享数据副本已创建(这些
    是实际传递回调用方的内容。

  • 此mex例程的临时分配列表上的所有内容都是
    销毁/释放。

  • 在这种背景下,持久性功能将执行以下操作:

    mexMakeMemoryPersistent(内存地址)

    • 从临时内存分配列表中删除memory_address

    mexMakeArrayPersistent(mxArray_address)

    • 从临时mxArray分配列表中删除mxArray_address
    • 将mxArray_address之后的mxArray的VariableType更改为0(正常)

    而且,实际上,mexMakeMemoryPersistent的文档规定以下内容:

    "如果您创建持久性内存,则在清除MEX函数时负责释放它。如果不释放内存,则MATLAB会泄漏内存。"

    最重要的是,您必须手动销毁/释放持久内存...一旦使内存持久,MATLAB内存管理器将不再为您提供帮助。当从内存中清除mex函数时,尤其如此。无论您是否有全局变量存储持久性存储器,也不管它最初来自于官方的MATLAB API函数,您的持久性内存都将泄漏。您需要使用mexAtExit和/或mexLock / mexUnlock函数的某种组合来管理这种情况,以免发生内存泄漏。据我所知,这一直是这些持久功能的行为。

    侧面注意:没有官方的API函数可以执行相反的操作...,即,您不能再次使持久性存储器成为非持久性。一旦使某些东西持久化,就必须坚持下去,必须手动对其进行处理。

    具有100MB内存块的演示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* persist_test.c */
    #include"mex.h"
    char *cp = NULL;
    #define ONE_MB (1024*1024)
    void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
    {
        if( cp == NULL ) {
            cp = mxMalloc(100*ONE_MB);
            mexMakeMemoryPersistent(cp);
        }
    }

    在命令行上,即使从内存中清除了mex例程(内存使用量不断增加而从未减少),也清楚地显示了内存泄漏:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    >> memory
    Maximum possible array:        2324 MB (2.436e+09 bytes) *
    Memory available for all arrays:        2324 MB (2.436e+09 bytes) *
    Memory used by MATLAB:        1012 MB (1.061e+09 bytes)
    Physical Memory (RAM):        8056 MB (8.447e+09 bytes)

    *  Limited by System Memory (physical + swap file) available.
    >> persist_test
    >> memory
    Maximum possible array:        2183 MB (2.289e+09 bytes) *
    Memory available for all arrays:        2183 MB (2.289e+09 bytes) *
    Memory used by MATLAB:        1115 MB (1.169e+09 bytes)
    Physical Memory (RAM):        8056 MB (8.447e+09 bytes)

    *  Limited by System Memory (physical + swap file) available.
    >> [~,mexnames] = inmem

    mexnames =

        'winqueryreg'
        'persist_test'

    >> clear persist_test
    >> [~,mexnames] = inmem

    mexnames =

        'winqueryreg'

    >> memory
    Maximum possible array:        2174 MB (2.279e+09 bytes) *
    Memory available for all arrays:        2174 MB (2.279e+09 bytes) *
    Memory used by MATLAB:        1103 MB (1.157e+09 bytes)
    Physical Memory (RAM):        8056 MB (8.447e+09 bytes)

    *  Limited by System Memory (physical + swap file) available.
    >>
    >> % Do it again
    >>
    >> persist_test
    >> memory
    Maximum possible array:        2053 MB (2.153e+09 bytes) *
    Memory available for all arrays:        2053 MB (2.153e+09 bytes) *
    Memory used by MATLAB:        1206 MB (1.265e+09 bytes)
    Physical Memory (RAM):        8056 MB (8.447e+09 bytes)

    *  Limited by System Memory (physical + swap file) available.
    >> [~,mexnames] = inmem

    mexnames =

        'winqueryreg'
        'persist_test'

    >> clear persist_test
    >> [~,mexnames] = inmem

    mexnames =

        'winqueryreg'

    >> memory
    Maximum possible array:        2073 MB (2.174e+09 bytes) *
    Memory available for all arrays:        2073 MB (2.174e+09 bytes) *
    Memory used by MATLAB:        1202 MB (1.260e+09 bytes)
    Physical Memory (RAM):        8056 MB (8.447e+09 bytes)

    *  Limited by System Memory (physical + swap file) available.
    >>