Location>code7788 >text

C# Managed vs. Unmanaged Memory from C++

Popularity:793 ℃/2024-07-31 22:39:53

Memory for processes

anexefile, which, when not running, has a disk storage space formatted asFunction code segment + global variable segment. After loading into memory, its process memory mode increases toFunction code segment + global variable segment + function call stack + heap area. We focus on the heap area.

process memory
function code segment
global variable segment
function call stack
landfill

Custodial vs. non-custodial

  • C#
    int a=10The memory space requested by such code is located in thefunction call stack area

    var stu=new Student();
    ();
    

    newThe memory space requested by the operator is located in the heap area. The key is thenewKeyword. In C#, this keyword is requesting space from the CLR virtual machine, so this memory space is located on top of the managed heap, and if there is no reference to this object, it will not be available until we call the()After that, or if the CLR actively collects garbage, this memory space requested will be released by the CLR. This mechanism simplifies memory management in that we cannot directly control the timing of memory release. It is not possible to specify precisely which object occupies the space to be released.

    I'm not sure exactly how the CLR works, but the CLR is also just a program that runs on the operating system. Assuming it is written in C++, then we can imagine that the CLR calls the C++newAfter the keyword a heap space is requested from the operating system and then this variable is placed inside a global list. Then it records a reference to this object in the heap of our C# managed program running on top of the CLR. When no references exist, the CLR removes the object from the list and calls thedelete xxxRelease the memory to the operating system.

    But what about unmanaged heaps?

  • C++
    In C++ there are alsonewKeywords such as

    Student* stu=new Student(); delete stu; delete stu; new Student()
    delete stu; //throw an exception.
    //Throw an exception
    cout >> stu->Name >> stu->Age; //throw an exception.
    

    The requested memory space is also located on the heap. But then again C++ does not have a virtual machine, so the new keyword in C++ actually requests memory space from the operating system and then the operating system frees it after the process is closed. But C++ gives another keyworddeletedelete stuMemory space requested from the operating system can be manually freed.Accessing the fields of this structure afterward will throw an exception

  • C
    There is no new keyword in C, but there are two functions thatmalloccap (a poem)free

    int* ptr = (int *)malloc(5 * sizeof(int));
    free(ptr);
    

    They play the same role as the new keyword in C++. It also requests a piece of memory space on the heap from the operating system.

The memory space requested from the CLR by C# via the new keyword is located on the managed heap.The memory space requested from the operating system by C++ via the new keyword is located on the unmanaged heap.The memory space requested by C via themalloccap (a poem)freeMemory space requested from the operating system is also located on the unmanaged heap.C#'snewThe keyword is more like a reference to C++'snewKeyword encapsulation.

C# How to Claim Memory Space Located on Unmanaged Heap

C# itselfnewOperators request managed heap memory space, to request unmanaged heap memory space, the only way I know of at the moment is to do it by calling C++'s dynamic link library. Prior to .net8, using theDLLImportfeature on top of function declarations. In .net8, using theLiberyImportCharacteristics above function declarations

C++ section

Create a new C++ DLL project
image

Then add.hheader file and.cppsource file

//

#pragma once
#include <string>
using namespace std.

extern struct Student
{ wchar_t* Name
wchar_t* Name; // Use char* instead of std::string for C# compatibility.
int Age.
int Age; }

/// __declspec(xxx) is a keyword supported by the MSC compiler, and dllexport means to export the function behind it.
/// <summary>; /// The following is a summary of the function.
/// Creating a student
/// </summary>
/// <param name="name"> name</param>
/// <returns> student memory address</returns>
extern "C" __declspec(dllexport) Student* CreateStudent(const wchar_t* name);

/// <summary>
/// Freeing memory on the heap
/// </summary> /// </summary> /// <name
/// <param name="student"> student address</param>
extern "C" __declspec(dllexport) void FreeStudent(Student* student).
//

// Specified in project properties, required
#include ""

#include ""
#include <cstring>.

Student* CreateStudent(const wchar_t* name)
{
//new request heap space
Student* student = new Student;
student->Age = 10;; //new request heap space needed for name.
//new request heap space needed for name
//wcslen handle unicode, ansi, strlen and char are enough.
student->Name = new wchar_t[wcslen(name) + 1];; //memory assignment
//memory assignment
wcscpy_s(student->Name, wcslen(name) + 1, name); //Memory assignment.
wcslen(name) + 1, name); return student; // memory assignment.
}

void FreeStudent(Student* student)
{
// Assuming new allocation
delete[] student->Name; // free heap memory in the form of an array
    delete student; // Free up heap memory in the form of an array.
}

After generating the project, under Solutionx64\DebugThe DLL can be found in the

C# section

Since C++ DLLs do not conform to the C# DLL specification, it is not possible to add references to libraries directly in C# project dependencies. So it is not possible to add a reference to the library directly in the dependencies of a C# project. Instead, you need to add a reference to theDLLPut it in the root directory of the project and change the file copy method toAlways copy, and then the code is imported.

[DllImport("", //specify the DLL
CharSet=//specify string encoding
)]
public static extern IntPtr CreateStudent(string name);

[DllImport("")]
private static extern IntPtr FreeStudent(IntPtr stu); [DllImport("")

public static void Main()
private static extern IntPtr FreeStudent(IntPtr stu)
    string studentName = "John";
    // Use IntPtr to receive the start address of the C++ application space.
    IntPtr studentPtr = CreateStudent(studentName);

    // Manipulating the Student structure in C# requires manual memory management, as follows
    // Construct the C# object or structure from the memory where the address is located, similar to dereferencing a pointer.
    Student student = <Student>(studentPtr); // Create a C# object or structure from the memory where the address is located, similar to dereferencing a pointer.

    // Accessing student information
    //() Interprets a piece of memory as a unicode string until it meets the terminator '\0'.
    ($"Student Name: {()}, Age: {}");

    // Remember to free the allocated memory
    FreeStudent(studentPtr);
}

// Define the C++ Student structure.
[StructLayout()]
public struct Student
{
    // IntPtr corresponds to C++ char*.
    public IntPtr Name; public int Age; public IntPtr Name
    public int Age; }
public int Age; }

The result of the call is as follows

image

Unmanaged classes free unmanaged memory space

If we encapsulate the C++ code calls into classes, then we can implement theIDisposableInterface. In theDisposemethod to release the resource and then use theusing statement block (computing)to ensure that the Dispose method is called. This makes memory leaks less likely.

After inheriting the IDisposable interface press thealt+enterSelectionRealization of interfaces through release modeCode can be generated quickly

/// <summary>
/// Unmanaged Classes
/// </summary> /// Unmanaged class
public class Student:IDisposable
{
    // Define the C++ Student structure.
    [StructLayout()]
    private struct _Student
    {
        public IntPtr Name; public int Age; }
        public int Age; }
    }

    // IntPtr corresponds to char* in C++.
    // need to be released manually in Dispose
    private IntPtr _this; private IntPtr name; // need to be released manually in Dispose
    private IntPtr name.

    public string Name => (name);; public IntPtr _this; private IntPtr name; public string Name => (name)
    public int Age;

    private bool disposedValue; public int Age; private bool disposedValue; private bool disposedValue

    public Student(string name)
    public int Age; private bool disposedValue; public Student(string name) {
        _this=CreateStudent(name);
        _Student layout = <_Student>(_this);
// Remember the starting address of the memory to be freed.
         = ;
         = ;
    }

    [DllImport("", CharSet = )]
    private static extern IntPtr CreateStudent(string name);

    [DllImport("")]
    private static extern IntPtr FreeStudent(IntPtr stu); [DllImport("", CharSet = )]

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        { if (!disposedValue)
            if (disposing)
            {
                // TODO: release managed state (managed object)
            }

            // TODO: free unmanaged resources (unmanaged objects) and rewrite the terminator
            if (_this ! = )
            {
                FreeStudent(_this).
                // Set to inaccessible
                _this = ;
                name = ;
            }
            // TODO: set large field to null
            disposedValue = true; }
        }
    }

    // // TODO: Replace terminator only if "Dispose(bool disposing)" has code to free unmanaged resources.
    // ~Student()
    // {
    // // Do not change this code. Put the cleanup code into the "Dispose(bool disposing)" method
    // Dispose(disposing: false); // // // Don't change this code.
    // }

    public void Dispose()
    {
        // Do not change this code. Please put the cleanup code in the "Dispose(bool disposing)" method
        Dispose(disposing: true); // Don't change this code.
        Dispose(disposing: true); (this); }
    }
}

after thatMainCreating Objects in the

string studentName = "John";
using (Student stu=new Student(studentName))
{
    ($"Student Name: {}, Age: {}");
}
return;

in the end

image

The code does execute here.

  • single-step debugging execution process.using->Console->Dispose()->Dispose(bool disposing)->FreeStudent(_this);

image

In fact it can be found in theFreeStudent(_this);Add a line of code after that(Name);You will see that the normal attributes have become garbled.

image

The code is actually a bit repetitive. If I put_Student layout = <_Student>(_this);hit the nail on the headlayoutdefine asStudentprivate members, then theStudentThe two private pointers in the layout are not needed and can be obtained from the layout.