【问题标题】:When a MEX file is called using mexMakeMemoryPersistent, how are variables re-assigned their pointers on subsequent calls?当使用 mexMakeMemoryPersistent 调用 MEX 文件时,变量如何在后续调用中重新分配其指针?
【发布时间】:2019-06-12 22:43:30
【问题描述】:

我正试图从 MATLAB 的 C MEX API 中了解mexMakeMemoryPersistent()

我不明白 - 当一个 MEX 文件被多次调用时,mexMakeMemoryPersistent() 已被使用,当它被第二次调用时,它分配的内存是如何归还给 MEX 文件的?

例如,假设我有一个名为 myFunc 的 MEX 文件

它包含以下内容:

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 将获取一些内存并获取指向它的指针,等等。

但是在它返回之后又如何呢?然后被称为 second 时间:

fx>> myFunc()

我们告诉 MATLAB 不要破坏我们刚刚分配给 myVar 的内存。但是当myFunc 的新实例运行时,该指针如何重新分配给myVar?如果没有,myVar 将只是NULL,我们将回到第一方。它是否维护变量名称列表和分配给它们的内存?但是如果我们有奇怪的范围规则、继承(在 C++ 的情况下)等会发生什么?它如何解析什么变量需要被传递给那个指针,并实际进行切换?

我使用它没有问题,我只是想从概念上理解它,因为我认为它真的很整洁。

【问题讨论】:

    标签: c matlab memory-management mex


    【解决方案1】:

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

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

    当您在 MATLAB 中执行 clear mexclear all 时,MEX 文件将从内存中卸载。全局变量将在此时停止存在。 由于您使用mxCalloc 分配内存,MATLAB 可以回收丢失的内存。如果您改用calloc,此时您将泄漏内存。您不再拥有指向已分配内存的指针,因此此时您已泄漏内存(请参阅下面的James' answer)。

    下次调用 MEX 文件时,将与第一次调用时一样。


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

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


    几个案例澄清一下:

    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 文件调用中保留,但不是指向的内存。第二次调用 MEX 文件时,myVar[0] *= 2 将执行非法操作,并可能导致 MATLAB 崩溃。

    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,因此每次都会分配新内存。内存是持久的,所以最终你会用完内存。

    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 分配的内存仍然存在。您再次泄漏内存。如果你想这样做,你需要注册一个在 MEX 文件存在时运行的函数,使用mexAtExit()

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

    在上面的例子中,我们使用一个静态变量来保存我们所有的数据,没有使用动态内存分配,我们不需要担心内存泄漏。我推荐这个,除非数组非常大。

    【讨论】:

    • 谢谢!我没有意识到它在完成后仍保留在内存中,我认为 MEX 文件已被擦除,但这是有道理的,因为否则 clear mex 不会做太多事情。但是 MEX 文件的其余状态都丢失了,对吧?就像局部变量和没有被告知是 mexMemoryMakePersistent() 的任何全局变量,以及程序的任何其他状态,对吧?
    • @Tyler: mexMemoryMakePersistent 是告诉 MATLAB 不要自动释放使用 mxCalloc 等创建的内存。否则,MATLAB 会自动清理所有分配的内存。如果您使用malloc 分配某些内容,那么 MATLAB 对它一无所知并且不会清理它——它会自动持久化。
    • 然后是全局变量(在任何函数范围之外定义的)、静态变量(在函数范围内声明的static)和局部变量(在函数内声明的所有其他变量)之间的区别)。其中,后一种类型不会在函数调用之间持续存在,因此不会在对 MEX 文件的调用之间持续存在。其他两种类型确实存在。 C 的常规作用域规则在这里适用,将 MATLAB 视为程序中的 mainmexFunction
    • 这是否意味着如果有人说,一个普通的全局 c++ int,它会在调用 MEX 文件之间停留?目前,为了维护这样一个 int,我实际上是为它 mxMalloc'ing 空间并使其持久化。如果我不需要这样做,那就太好了。
    • @Chris:你关于 mxCalloc 和朋友们从不泄露的说法是不正确的。请参阅我的答案和示例。
    【解决方案2】:

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

    mxArray 变量标题有一个字段,我将其称为 VariableType,其中包含一个值,指示它是变量的类型。例如,对于 R2018b 及更早版本:

    struct mxArray_header {
        void *RevCrossLink;
        mxClassID ClassID;
        int VariableType;
        :
        etc.
    

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

    所有 (*) 分配内存的官方 API 函数(mxArray 变量或原始内存)将该内存的地址放在 mex 例程的 MATLAB 内存管理器临时分配列表中。注意:mxArray 变量的“数据”内存(即 mxGetPr( ) 和朋友背后的东西)不在此分配列表中 ...它的处置完全取决于它所在的 mxArray一部分。使用官方API函数创建的mxArrays的VariableType为4(临时)。

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

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

    1. 创建了 plhs[ ] 变量的共享数据副本(这些 是实际传回给调用者的内容)。

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

    在此背景下,持久函数的作用如下:

    mexMakeMemoryPersistent(memory_address)

    • 从临时内存分配列表中删除 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 内存块的演示:

    /* 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 例程,也清楚地显示内存泄漏(内存使用量不断上升,从不下降):

    >> 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.
    >> 
    

    【讨论】:

    • 感谢您澄清这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 1970-01-01
    • 2021-07-12
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多