【问题标题】:Memory Stress Testing in C#C# 中的内存压力测试
【发布时间】:2011-12-02 04:25:51
【问题描述】:

我想创建一个工具来模拟内存限制以对其他应用程序进行内存压力测试。经过一些谷歌搜索后,我想出了以下代码,但是在运行此代码时,任务管理器或资源监视器不会显示内存使用量的任何差异。只是一条平线。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Win32Tute
{
    unsafe class Program
    {
        // Heap API flags
        const int HEAP_ZERO_MEMORY = 0x00000008;
        // Heap API functions
        [DllImport("kernel32")]
        static extern int GetProcessHeap();
        [DllImport("kernel32")]
        static extern void* HeapAlloc(int hHeap, int flags, int size);
        [DllImport("kernel32")]
        static extern bool HeapFree(int hHeap, int flags, void* block);

        private static int ph = GetProcessHeap();

        public static void* Alloc(int size)
        {
            void* result = HeapAlloc(ph, HEAP_ZERO_MEMORY, size);
            if(result == null) throw new OutOfMemoryException("Couldn't execute HeapAlloc");
            return result;
        }

        public static void Free(void* block)
        {
            if(!HeapFree(ph, 0, block)) throw new InvalidOperationException("Couldn't free memory");
        }

        public static void Main(string[] args)
        {
            int blockSize = 1024*1024; //1mb
            byte*[] handles = new byte*[1024];
            Console.WriteLine("Memory before : " + (Process.GetCurrentProcess().PrivateMemorySize64/1024)/1024); // get value in Megabytes
            try
            {
                for(int i=0; i<1024; i++)
                {
                   handles[i] = (byte*)Alloc(blockSize);

                }
            }
            finally
            {
                Console.WriteLine("Memory after  : " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024)/1024);
                Console.WriteLine("Finished allocating 1024MB memory....Press Enter to free up.");
                Console.ReadLine();
            }

            try
            {
                for(int i=0; i<1024; i++)
                {
                    Free(handles[i]);
                }
            }
            finally
            {
                Console.WriteLine("Memory at the end : " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024)/1024);
                Console.WriteLine("All allocated memory freed. Press Enter to quit..");
                Console.ReadLine();
            }
        }
    }
}

【问题讨论】:

  • 我试过你的代码,HeapAlloc 正在页面文件上分配内存,而在物理内存上没有。在 Resource Monitor 中,Commit KB 变为 ~1GB,如果您看到 Process.PagedMemorySize64 属性,它也显示 ~1GB。所以问题是如何强制 HeapAlloc 在物理内存上分配内存,而不是页面文件。
  • 我尝试了 Marshal 类和 AllocHGlobal 的差异代码。起初它也给出了相同的结果。图表没有变化。然后我在 AllocHGlobal 之后调用了 Marshal.Copy()。然后只有内存分配反映在监视器中。所以我猜只是分配并不会真正从物理内存中保留空间。
  • 我是内存管理新手。所以我不得不盲目地尝试一切。 :)
  • 为什么不只是var foo = new byte[100000000]; (x10)?
  • 您可以尝试将一些数据写入您正在分配的内存。如果您不使用地址空间,它可能永远不必将其交换到物理内存中。您可能还想查看 VirtualLock 函数。您可以使用它将进程的虚拟地址空间锁定到物理内存中。

标签: c# memory-management stress-testing


【解决方案1】:

这种事情几乎总是一个坏主意。如果您成功地创建了一个消耗内存的程序,您可能会发现这样做并不会阻止其他程序响应。虚拟内存管理器将竭尽全力保持其他程序的运行。例如,它会将内存占用的数据分页到磁盘,以便将工作程序的数据保存在它所属的内存中。如果你修改你的内存占用,让它锁定内存中的页面(即不让页面被换出),那么计算机将开始抖动。

最好在您正在测试的程序中编写诊断代码,这样您就可以调用SetProcessWorkingSetSizeEx 来测试程序在不同的内存条件下如何响应。如果你不能修改你正在测试的程序,你可以编写一个程序来获取测试程序的句柄并调用SetProcessWorkingSetSizeEx,传递那个句柄。

【讨论】:

    【解决方案2】:

    好的,你的帖子中有三个问题......

    1. 不需要PInvoke分配,你可以简单地使用Marshal.AllocHGlobalFreeHGlobal

    2. 如果您实际上不使用内存,则系统将保留它,但不一定将其放入工作集中。因此,如果您想要 HOG 内存,您将需要首先分配内存,然后不断地读取/写入该内存。即使这样,系统也可以卸载未分配的页面,但会对内存造成压力。

    3. 一般来说,我必须同意 Jim Mischel 的观点,这对你帮助不大。

    对于它的价值,这里的程序更新为写入所有分配的内存,然后启动一个持续读取内存的线程。不利的一面是它会消耗一个线程/cpu。

    static void Main(string[] args)
    {
        int blockSize = 1024*1024; //1mb
        byte*[] handles = new byte*[1024];
        Console.WriteLine("Memory before : " + (Process.GetCurrentProcess().PrivateMemorySize64/1024)/1024); // get value in Megabytes
        try
        {
            for(int i=0; i<1024; i++)
            {
                handles[i] = (byte*)Marshal.AllocHGlobal(blockSize);
                //write to the memory
                for (int off = 0; off < blockSize; off++)
                    *(handles[i] + off) = 1;
            }
        }
        finally
        {
            //create a thread to ensure the memory continues to be accessed
            ManualResetEvent mreStop = new ManualResetEvent(false);
            Thread memoryThrash = new Thread(
                () =>
                    {
                        int ihandle = 0;
                        while (!mreStop.WaitOne(0, false))
                        {
                            for (int off = 0; off < blockSize; off++)
                                if (*(handles[ihandle++ % handles.Length] + off) != 1)
                                    throw new InvalidOperationException();
                        }
                    }
                );
            memoryThrash.IsBackground = true;
            memoryThrash.Start();
    
            Console.WriteLine("Memory after  : " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024)/1024);
            Console.WriteLine("Finished allocating 1024MB memory....Press Enter to free up.");
            Console.ReadLine();
    
            mreStop.Set();
            memoryThrash.Join();
        }
    
        try
        {
            for(int i=0; i<1024; i++)
            {
                Marshal.FreeHGlobal(new IntPtr(handles[i]));
            }
        }
        finally
        {
            Console.WriteLine("Memory at the end : " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024)/1024);
            Console.WriteLine("All allocated memory freed. Press Enter to quit..");
            Console.ReadLine();
        }
    }
    

    【讨论】:

    • if (*(handles[ihandle++ % handles.Length] + off) != 1) 是故意的,即您总是每个块只读取一个字节吗?
    • @ThomasWeller 唯一的阅读点是加载内存页面,所以阅读多少并不重要。
    • 我同意,所以off++ 循环可以优化为循环off+=pageSize 步骤。此外,只初始化每个页面的第一个字节就足以使程序启动得更快。
    • @ThomasWeller 是的,同意。
    猜你喜欢
    • 2011-12-08
    • 2012-06-05
    • 2014-09-30
    • 1970-01-01
    • 2014-05-31
    • 2011-08-11
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多