C#中的手动内存管理器 - 只是一项研究

时间:2016-07-18 14:19:37

标签: c# winapi memory-management

我正在做一些研究,我实现了一个简单的内存管理器系统来查看进程的行为,但我注意到了MemoryHelper类的免费方法,以及" Marshal.FreeHGlobal"没有意识到内存中的空闲,即使在释放内存后也可能访问数据......

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication3
{
    public unsafe static class MemoryHelper
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr GetProcessHeap();

        [DllImport("kernel32.dll", SetLastError = false)]
        static extern void* HeapAlloc(IntPtr hHeap, uint dwFlags, UIntPtr dwBytes);

        [DllImport("kernel32.dll")]
        static extern void* HeapReAlloc(IntPtr hHeap, uint dwFlags, IntPtr lpMem, UIntPtr dwBytes);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool HeapFree(IntPtr hHeap, uint dwFlags, void* lpMem);

        private static uint HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
        private static uint HEAP_NO_SERIALIZE = 0x00000001;
        private static uint HEAP_ZERO_MEMORY = 0x00000008;
        private static IntPtr Handle { get; set; } = GetProcessHeap();

        public static void* Malloc(uint size)
        {
            var tmp = HeapAlloc(Handle, HEAP_ZERO_MEMORY, new UIntPtr(size));
            if (tmp == null)
                throw new Exception("Fail to allocate memory");
            return tmp;
        }

        public static void* Realloc(IntPtr blk, uint newSize)
        {
            var tmp = HeapReAlloc(Handle, HEAP_REALLOC_IN_PLACE_ONLY, blk, new UIntPtr(newSize));
            if (tmp == null)
                throw new Exception("Fail to reallocate memory");
            return tmp;
        }

        public static bool Free(void* blk)
        {
            var ret = HeapFree(Handle, HEAP_NO_SERIALIZE, blk);
            if (!ret)
                throw new Exception("Fail to free memory");
            return ret;
        }
    }

    unsafe public class Study
    {
        public Study()
        {
            UsingManualMemoryClass();
            Console.WriteLine("".PadLeft(80, '='));
            UsingMarshalClass();
        }

        private static readonly object LockObj = new object();
        public void UsingManualMemoryClass()
        {
            int tamanho = 20;
            int* teste = (int*)MemoryHelper.Malloc((uint)(sizeof(int) * tamanho));

            Random rnd = new Random();
            lock (LockObj)
            {
                for (int i = 0; i < tamanho; i++)
                    teste[i] = rnd.Next(0,8000);
            }

            for (int i = 0; i < tamanho; i++)
                Console.WriteLine(teste[i]);

            MemoryHelper.Free(teste);

            Console.WriteLine("".PadLeft(80, '-'));
            for (int i = 0; i < tamanho; i++)
                Console.WriteLine(teste[i]);
        }

        public void UsingMarshalClass()
        {
            int tamanho = 20;
            int* teste = (int*)Marshal.AllocHGlobal(sizeof(int) * tamanho);

            Random rnd = new Random();
            lock (LockObj)
            {
                for (int i = 0; i < tamanho; i++)
                    teste[i] = rnd.Next(0, 8000);
            }

            for (int i = 0; i < tamanho; i++)
                Console.WriteLine(teste[i]);

            Marshal.FreeHGlobal((IntPtr)teste);

            Console.WriteLine("".PadLeft(80, '-'));
            for (int i = 0; i < tamanho; i++)
                Console.WriteLine(teste[i]);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            new Study();
        }
    }
}

为什么会这样?

-----编辑

我在codeproject找到了这篇文章,我得到了C ++ / CLI代码,用代码创建了一个简单的DLL并做了一些更改,以使用本机C运行时来分配和释放内存

#include <malloc.h>
#include <stdlib.h>>

using namespace System;

namespace mm
{
    public ref class Unmanaged abstract sealed
    {
    public:
        generic <typename T> where T : value class
            static void* New(int elementCount)
        {
            return malloc(sizeof(T) * elementCount);
        }

        static void Free(void* unmanagedPointer)
        {
            free(unmanagedPointer);
        }

        generic <typename T> where T : value class
            static void* Resize(void* oldPointer, int newElementCount)
        {
            return realloc(oldPointer, (int)sizeof(T) * newElementCount);
        }
    };
}

使用本机C函数执行此操作后,我无法在免费...

之后访问内存

1 个答案:

答案 0 :(得分:0)

Marshal.FreeHGlobal调用Win32 API函数LocalFree。如果在调用LocalFree后尝试访问内存,则调用未定义的行为。一些可能的结果:

  • 您可能会遇到运行时错误。
  • 您可能仍然可以访问之前状态的内存。
  • 您可能仍然可以访问内存但已被修改,因为它已被重复使用。
  • ...

几乎任何事情都可能发生,你不能依赖任何东西。

你的错误是推断释放后能够访问内存意味着内存尚未被释放。这种合理的推论是错误的。