wxzself经验分享 内存管理

0
(0)

内存的分配管理也是操作系统的一个重要功能。里面有物理内存管理、虚存管理、应用程序内存管理,还有运行工作集、缓冲区的管理,也是内存管理的延续。我们这里以应用程序的内存管理为例,实现一个具有 Malloc 和 Free 功能的 API。由于内存的分配释放肯定要和物理地址打交道,所以我们需要将地址保存到数据表中,MemoryAlloc 表的 BaseAddress 字段存放的就是真实地址(转换为64位整数)。

这个例子使用的是伙伴式内存分配,使用首先匹配算法。

开发运行环境:.Net Framework 4.5,VS2013,SQL Server 2008R2,Entity Framework 5.0, 必须是64位。

1)内存分配伙伴算法流程:

2)内存释放伙伴算法流程:

1 程序运行

在开发阶段,还需要做以下事情:

  1. 建立数据表 MemoryAlloc 结构见下:
NameTypeSizeDefaultAllowNullPK备注
BaseAddressbigint  falseTrue内存基址
Statusnvarchar(1)1 False 状态(A:Allocate, F:Free)
Lengthbigint 0false 长度

2)使用Entity framework 5.0 的EDM框架,通过数据库生成实体及容器。

3)编译代码,执行 MemoryManager.exe。运行结果见后面的截图。

2 关键代码

//内存管理全部代码(MemoryMan.cs):

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace MemoryManager
{

    

    /// <summary>
    /// 内存管理对象
    /// </summary>
    public unsafe class MemoryMan
    {
        /// <summary>
        /// 公开构造
        /// </summary>
        public MemoryMan()
        {
            Ent = new MemoryDBEntities();
            Buffer = IntPtr.Zero;
        }

        /// <summary>
        /// Entity 容器
        /// </summary>
        public virtual MemoryDBEntities Ent { get; internal protected set;}
       
        public static string Alloced = "A";   // 已经申请
        public static string Freed = "F";    // 空闲

        /// <summary>
        /// 内存缓冲区
        /// </summary>
        public IntPtr Buffer { get; set;}


        /// <summary>
        /// 是否初始化
        /// </summary>
        public bool IsInit { get; set; }

        /// <summary>
        /// 初始化内存管理器
        /// </summary>
        /// <param name="initSize">全部管理内存的大小</param>
        public void Init(int initSize)
        {
            if (IsInit)
                return;
            try
            {
                Buffer = Marshal.AllocHGlobal(initSize);
                ClearData();    
                MemoryAlloc ma = new MemoryAlloc();
                ma.BaseAddress = Buffer.ToInt64();
                ma.Status = Freed;
                ma.Length = initSize;
                Ent.Set<MemoryAlloc>().Add(ma);
                Ent.SaveChanges();
                IsInit = true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 清空数据库数据,内部方法
        /// </summary>
        private void ClearData()
        {
            Ent.Database.ExecuteSqlCommand("delete from MemoryAlloc");
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public void Close()
        {
            if (!IsInit)
                return;
            try
            {
                if (Buffer != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(Buffer);
                    Buffer = IntPtr.Zero;
                }
                ClearData();
                IsInit = false;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 申请内存,无或失败返回 IntPtr.Zero
        /// </summary>
        /// <remarks>
        /// 使用方法: byte* pbytes = (byte*)Malloc(1024)
        /// </remarks>
        /// <param name="size">要申请的大小</param>
        /// <returns>地址</returns> 
        public IntPtr Malloc(int size)
        {
            if (size == 0)
                return IntPtr.Zero;
            var result = Ent.MemoryAlloc.Where(x => (x.Length >= size) && (x.Status == Freed)).
                OrderBy( x => x.BaseAddress).FirstOrDefault();
            if (result == null)         // 未找到
                return IntPtr.Zero;
          
            IntPtr ret = new IntPtr(result.BaseAddress);
            // 申请内存
            result.Status = Alloced;
            if (result.Length == size)  // 大小合适,正好分配,无需其它操作
            {                             
            }
            else                        // 偏大,分裂为两个,第一个分配,第二个空闲。          
            {
                long oldlen = result.Length;
                IntPtr newaddr = IntPtr.Add(ret, size);
                result.Length = size;                
                MemoryAlloc ma = new MemoryAlloc();
                ma.BaseAddress = newaddr.ToInt64();
                ma.Status = Freed;
                ma.Length = oldlen - size;
                Ent.Set<MemoryAlloc>().Add(ma);                
            }
            Ent.SaveChanges();
            return ret;
        }

        /// <summary>
        /// 释放内存,忽略其它无效情况
        /// </summary>
        /// <param name="freePtr">要释放的地址</param> 
        public void Free(IntPtr freePtr)
        {
            if (freePtr == IntPtr.Zero)
                return;
            var freebase = freePtr.ToInt64();
            var me = Ent.MemoryAlloc.Where ( x => (x.BaseAddress == freebase) && (x.Status == "A")).FirstOrDefault();
            if (me == null)        // 未找到
                return;

            // 释放内存。
            me.Status = Freed;

            // 合并右边邻接空闲块。
            var combinRight = Ent.MemoryAlloc.Where(x => ((x.Status == Freed) && (x.BaseAddress > freebase))).
                                OrderBy( x => x.BaseAddress).FirstOrDefault();
            if (combinRight != null)
            {
                if ((me.BaseAddress + me.Length) == combinRight.BaseAddress)
                {
                    me.Length += combinRight.Length;
                    Ent.MemoryAlloc.Remove(combinRight);
                }
            }

            // 合并左边邻接空闲块。
            var combinLeft = Ent.MemoryAlloc.Where(x => (x.Status == Freed) && (x.BaseAddress < freebase)).
                              OrderByDescending( x => x.BaseAddress).FirstOrDefault();
            if (combinLeft != null)
            {
                if ((combinLeft.BaseAddress + combinLeft.Length) == me.BaseAddress)
                {
                    combinLeft.Length += me.Length;
                    Ent.MemoryAlloc.Remove(me);
                }
            }
            Ent.SaveChanges();


        }   
    }
}
主窗体相关代码(MemoryMan.cs):
1>初始化:

/// <summary>
        /// 初始化内存管理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonInit_Click(object sender, EventArgs e)
        {
            mm.Init(int.Parse(maskedTextBoxInit.Text));
            LoadData();
            buttonMalloc.Enabled = buttonFree.Enabled =
                buttonFreeBySelected.Enabled = buttonMalloc10AndFill0.Enabled = mm.IsInit;
            buttonInit.Enabled = !mm.IsInit;
        }

2>申请 10 字节并填 0:


/// <summary>
        /// 申请10字节内存并填 0
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonMalloc10AndFill0_Click(object sender, EventArgs e)
        {
            int size = 10;
            var qptr = mm.Malloc(size);
            if (qptr == IntPtr.Zero)
                return;
            byte* pbytes = (byte*)qptr;
            if (pbytes == null)
                return;

            var qptrend = (byte*)(qptr + size);
            while (pbytes++ < qptrend)
                *pbytes = 0;

            LoadData();
        }

3 执行结果

1)程序的入口界面:

2)点击Init 后:

3)点击3 次 Malloc By Sze 申请内存:

(原文未截图)

4)输入第一块内存地址然后点击Free By Address:

5)输入第三块内存地址然后点击Free By Address:

6)在网格选中第二块内存地址然后点击 Free By Grid Selected:

7)点击 Malloc 10 And Fill 0:

4 扩展阅读

我们上面的例子是有关首先分配算法的,其实我们也可以实现其它分配算法,只要修改Linq查询语句的排序列就行:

排序列方向实现的算法
BaseAddress正向首先匹配
Length正向最佳匹配
BaseAddress反向最后匹配

这篇文章有用吗?

点击星号为它评分!

平均评分 0 / 5. 投票数: 0

到目前为止还没有投票!成为第一位评论此文章。

很抱歉,这篇文章对您没有用!

让我们改善这篇文章!

告诉我们我们如何改善这篇文章?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注