簡體   English   中英

Marshal.StructureToPtr無法解釋的內存泄漏

[英]Unexplained memory leak with Marshal.StructureToPtr

我正在通過C ++ / CLR包裝器開發一個涉及本機C ++到C#interop的應用程序。

我在執行以下操作時遇到問題,導致內存泄漏:

MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
Marshal.StructureToPtr(data, ptr, false);

(注意:我意識到我此時並沒有對“數據”做任何事情,所以這是多余的。)

內存使用率持續上升,直到應用程序因系統內存不足而崩潰。 當我刪除此代碼時,這不會發生。 它不是垃圾收集器,因為a)它應該在系統內存不足時收集並且b)我已經嘗試用GC.Collect()強制它。

實際上,我已將泄漏范圍縮小到StructureToPtr命令。

我無法將第三個參數設置為“true”,因為內存是由本機C ++分配的,而C#認為這個“受保護”的內存無法釋放。

我已經檢查了填充的Data結構是否完整,是否有有效數據,並且與等效的本機結構大小相同。

在我看來,這應該是應該發生的事情:

  1. 由ptr引用的我的本機結構被編組並復制到“數據”托管結構

  2. 管理結構被復制回ptr引用的相同內存。

我無法看到這會如何導致內存泄漏,因為它的結構大小完全相同,被復制回相同的內存空間。 但顯然它確實如此,刪除代碼會堵塞漏洞。

這里有些機械師,我不能正確理解嗎?

編輯:根據要求,這里是“MyObject”的聲明。

C#:

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamTwelve;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamThirteen;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string ParamFourteen;
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

C ++:

struct MyObject
{
    public:
    bool ParamOne;
    bool ParamTwo;      
    bool ParamThree;
    bool ParamFour;
    bool ParamFive;
    bool ParamSix;
    float ParamSeven;
    float ParamEight;
    float ParamNine;
    Vector2f ParamTen;
    Vector2f ParamEleven;
    wchar_t * ParamTwelve;
    wchar_t * ParamThirteen;
    wchar_t * ParamFourteen;
    void * ParamFifteen; 
    void * ParamSixteen;
};

Vector2f的定義如下:

[StructLayout(LayoutKind.Sequential)]
public struct Vector2f
{
    [MarshalAs(UnmanagedType.R4)]
    float x;
    [MarshalAs(UnmanagedType.R4)]
    float y;
}

你有指向結構中的字符串的指針。 那些指針是在你的非托管代碼中分配的(使用new wchar_t[<a number>] ),對吧? 在將這些指針編組到托管代碼時,封送程序會創建托管字符串並使用非托管字符數組的內容填充它們。 當將它們編組回非托管代碼時,封送程序會復制整個結構內容,包括字符指針,為它們分配新值(使用CoTaskMemAlloc()為每個字符串分配內存)。 這就是Marshal.StructureToPtr的第三個參數。 如果設置為true,則封送器會嘗試釋放字符指針指向的內存(使用CoTaskMemFree() )。 如果已使用new運算符為字符指針分配內存,則無法將該參數設置為true,並且通過編組返回到非托管,您將丟失指向已分配內存的指針(marshaller將使用新值覆蓋它們)。 並且由於您沒有釋放由marshaler分配的內存,因此最終會導致內存泄漏。

處理這種情況的最佳方法是:

將字符串作為指針並使用Marshal.PtrToStringUni()將它們轉換為字符串:

[StructLayout(LayoutKind.Sequential)]
public struct MyObject
{
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamOne;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamTwo;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamThree;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFour;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamFive;
    [MarshalAs(UnmanagedType.I1)]
    public bool ParamSix;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamSeven;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamEight;
    [MarshalAs(UnmanagedType.R4)]
    public float ParamNine;
    public Vector2f ParamTen;
    public Vector2f ParamEleven;
    public IntPtr ParamTwelve;    // <-- These are your strings
    public IntPtr ParamThirteen;  // <--
    public IntPtr ParamFourteen;  // <--
    public IntPtr ParamFifteen;
    public IntPtr ParamSixteen;
}

和編組:

    MyObject data = (MyObject)Marshal.PtrToStructure(ptr, typeof(MyObject));
    var str1 = Marshal.PtrToStringUni(data.ParamTwelve);
    var str2 = Marshal.PtrToStringUni(data.ParamThirteen);
    var str3 = Marshal.PtrToStringUni(data.ParamFourteen);
    Marshal.StructureToPtr(data, ptr, false);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM