UML软件工程组织

 

 

压力测试
 
作者:James McCaffrey 来源:microsoft
 

下载本文的代码: TestRun05.exe (116KB)

整体程序结构 整体程序结构
工具的详细介绍 工具的详细介绍
酷酷的控制台输出 酷酷的控制台输出
扩展工具 扩展工具
对它进行包装 对它进行包装

压力测试是一种基本的质量保证行为,它是每个重要软件测试工作的一部分。压力测试的基本思路很简单:不是在常规条件下运行手动或自动测试,而是在计算机数量较少或系统资源匮乏的条件下运行测试。通常要进行压力测试的资源包括内部内存、CPU 可用性、磁盘空间和网络带宽。要减少用于测试的资源,可运行一个称为压力器 (stressor) 的工具。

图 1 中的图像显示一种运行中的压力工具 EatMem。命名压力器时通常使用"Eat"前缀,后跟要使用的资源类型,因此减少内存、磁盘空间和降低 CPU 可用性的压力器可分别命名为 EatMem、EatDisk 和 EatCpu。EatMem 压力器是一个命令行程序,它接受一个指定运行时长的参数(以分为单位)。EatMem 大约每三秒钟尝试分配随机数量的可用内存。正如您在图 1 中看到的,内存请求可能失败。在这种情况下,EatMem 会一直尝试分配内存,直到成功为止。当然,该压力器工具自身并不执行任何实际测试;它只是准备一个进行测试的计算机状态。通过在测试实际程序的同时运行该程序,您可为应用程序模拟在常规测试环境中很难进行测试的条件。


 

图 1 运行中的 EatMem 压力器

整体程序结构

EatMem 压力器工具(用 C# 编写)的整体结构如图 2 所示。首先,我将一个 using 语句添加到 InteropServices 命名空间,这样就可以调用本机 Win32® 函数。特别地,我调用 GlobalAlloc 和 GlobalFree 函数来分配和释放内存,并调用 GlobalMemoryStatus 函数确定分配多少内存。我还使用了一些 Win32 API 函数来操作控制台文本的颜色。

Figure 2 EatMem Program Structure
using System;
using System.Runtime.InteropServices;

namespace EatMem
{
  class Class1
  {
    [DllImport("kernel32.dll")]
    extern static IntPtr GlobalAlloc(uint uFlags, uint dwBytes);

    [DllImport("kernel32.dll")]
    extern static IntPtr GlobalFree(IntPtr hMem);
    
    [DllImport("kernel32.dll")]
    extern static void GlobalMemoryStatus(ref MEMORYSTATUS lpBuffer);
    
    public struct MEMORYSTATUS
    {
      public  uint dwLength;
      public  uint dwMemoryLoad;
      public  uint dwTotalPhys;
      public  uint dwAvailPhys;
      public  uint dwTotalPageFile;
      public  uint dwAvailPageFile;
      public  uint dwTotalVirtual;
      public  uint dwAvailVirtual;
    }

    static MEMORYSTATUS memStatusBuffer = new MEMORYSTATUS();
    
    [STAThread]
    static void Main(string[] args)
    {
      try
      {
        // get number of minutes to run from command line
        // determine stopping time
        // print output header
        
        while (currentTime < stopTime) // main loop
        {
          for (int cycle = 0; cycle < 8; ++cycle)
          {
            // get the elapsed time so far

            // compute a random percentage
            // get current memory status
            // compute number bytes to eat
            
            // allocate memory
            // get current memory status
            // print current time, memory status

            // pause to give app under test stress time

            // free memory
          } // for loop
        } // main while loop
      }
      catch(Exception ex)
      {
        Console.WriteLine(«Fatal error: « + ex.Message);
        Console.ReadLine();
      }
    } // Main()
  } // class
} // ns

GlobalAlloc 和 GlobalFree 函数在 kernel32.dll 库中定义。通过使用 DllImport 属性,可从我的 C# 压力器程序调用它们。GlobalFree 的原始 C++ 签名是:

   HGLOBAL GlobalAlloc(UINT uFlags, 
       SIZE_T dwBytes);

返回类型 HGLOBAL 是众多 Win32 符号常量之一。它实际上是指向 GlobalAlloc 分配的第一个内存地址的指针,因此,我将该类型转换为 System.IntPtr 类型。IntPtr 是一个特定于平台的类型,用于表示指针或句柄。我将在下一节中更全面地解释 GlobalAlloc 及其搭档 GlobalFree。我使用 GlobalMemoryStatus 函数检索当前可用的内存量,这样,我就可以确定使用 GlobalAlloc 函数尝试分配多少内存了。GlobalMemoryStatus 的 Win32 签名是:

void GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);

将一个指向结构的指针/引用传递给 GlobalMemoryStatus,然后 GlobalMemoryStatus 使用内存信息填充该结构。该结构在图 2 中定义。该结构中最重要的字段是 dwAvailVirtual 字段,调用 GlobalMemoryStatus 函数之后,该字段将保留可用虚拟内存的字节量。

EatMem 工具基本上是一个有组织的 while 循环 - 它将进行迭代,直到当前系统日期/时间超出规定的日期/时间:通过将在命令行中指定的持续时间(以分为单位)加上该工具启动时的时间,确定该这一日期/时间。在主 while 循环内,有一个执行了 8 次的次循环。该次循环在功能上不是必需的;它能够较好地偶尔显示进度输出。

工具的详细介绍

现在,我们详细介绍一下 EatMem 工具的每个部分。我将在本专栏的下一节中讨论神奇的控制台输出,之后我将讨论针对基本代码的替代方案和一些修改。图 3 显示将运行 EatMem 的代码。

Figure 3 Initialize EatMem
const uint basicUnit = 1024; // 1 KB
Random r = new Random();

if (args.Length != 1 || !args[0].StartsWith("-d"))
{
  Console.WriteLine("\nExample usage: eatmem -d100");
  Console.Write("Randomly eats between 10% and 70%
  Console.WriteLine(" of virtual memory for 100 minutes");
}

// determine stopping time
int duration = int.Parse(args[0].Substring(2, args[0].Length-2));
TimeSpan ts = new TimeSpan(0, duration, 0); // hr, min, sec
DateTime startTime = DateTime.UtcNow;
DateTime stopTime = startTime + ts;

我首先声明一个常量 1024,因为我将以千字节进行所有的 EatMem 显示,同时这些计算将以字节执行。接下来实例化一个 Random 对象,这样我之后就可以生成伪随机数。

然后,我使用一项非常原始的技术获取 EatMem 应该运行的持续时间(以分为单位)。我的代码假设有一个命令行参数,它的确切形式是 -dN,其中 N 是数字。我将获取显示的数字,因为 args[0] 将表示诸如"-d123"的字符串,而 Substring(2) 将是"123"部分。我使用一项标准的技术来确定停止时间:我创建一个 TimeSpan 对象指示所需的分钟数,然后使用该 TimeSpan,其中重载的"+"操作符在 EatMem 启动时将应用到该时间。

在进入主 while 控制循环之前,我要做的最后一件事是显示该输出的简要标头以及以下几行代码:

// print output headers
WriteLine("Begin EatMem for " + duration + " minutes");
WriteLine("Consuming random 10%-70% of virtual memory");
WriteLine("Launch test harness while EatMem stressor runs");

WriteLine("      Available  Available    Current");
WriteLine("       Physical    Virtual     Memory");
WriteLine(" Time    Memory     Memory  Allocation   Pct.");
WriteLine("---------------------------------------------");

请注意,为简明起见,我稍微改动了实际语句。您可以在本专栏附带的代码下载中获取确切的语句。

正如您稍后将在本专栏中看到的,为您提供了大量的附加信息。由于压力器工具要修改运行它的计算机的状态,因此,这些压力器的所有输出在一定程度上是不必要的。要监控的最重要的数据是任何给定时间的可用虚拟内存量。

在主 while 循环和次循环中,我将经过的时间传入 TimeSpan 对象,并计算一个随机百分比,如下所示:

while (DateTime.UtcNow < stopTime) // main loop
{
  for (int cycle = 0; cycle < 8; ++cycle)
  {
    // get the elapsed time so far
    TimeSpan elapsedTime = DateTime.UtcNow - startTime;

    // compute a random percentage
    double randomPct = (r.NextDouble() * (0.70 - 0.10)) + 0.10;
    if (randomPct < 0.10 || randomPct > 0.70)
      throw new Exception("Bad random pct");

这里我对 EatMem 进行了硬编码,以便它可以使用一个标准映射技术生成 0.10 到 0.70 之间的一个随机数字。该方法调用 Random.NextDouble 将返回范围 [0.0, 1.0](这意味着大于或等于 0.0 而小于 1.0)中的一个数字。如果我用 (0.70 - 0.10) = 0.60 乘以该间隔,则该间隔将映射为范围 [0.0, 0.6] 中的一个随机数字。然后,如果我加上 0.10,则最终的间隔将映射为 [0.10, 0.70]。

下一步是计算要利用的字节数:

// compute number of bytes to eat
GlobalMemoryStatus(ref memStatusBuffer);
uint numBytesToEatInBytes =
  (uint)(randomPct * memStatusBuffer.dwAvailVirtual);
uint numBytesToEatInKB =
  numBytesToEatInBytes / basicUnit;

我使用 C# 互操作机制调用 Win32 GlobalMemoryStatus 方法,以便获取当前可用的虚拟内存字节数。请注意,我传入了一个指向 MEMORYSTATUS 结构的引用,这是因为初始 C++ 签名需要一个指向该结构参数的指针。或者要以托管代码表示,我传入一个对结构的引用,因为该结构将更改。当内存状态信息存入 memStatusBuffer 对象后,我可以将它拿出来使用。我可以确定要分配的字节数,方法是用在前一步中计算的随机百分比乘以可用字节数。为进行显示,我也将该结果转换成千字节。

现在,我准备尝试用 GlobalAlloc 函数分配内存(请参阅 图 4)。GlobalAlloc 函数接受两个参数。第一个参数是一个标志,指定若干种分配方式其中之一:

GMEM_FIXED    = 0x0000 Allocates fixed memory.
GMEM_MOVEABLE = 0x0002 Allocates movable memory.
GMEM_ZEROINIT = 0x0040 Initializes memory contents to 0.

GPTR = 0x0040 Combines GMEM_FIXED and GMEM_ZEROINIT.
GHND = 0x0042 Combines GMEM_MOVEABLE and GMEM_ZEROINIT.

这里我使用 GPTR = 0x0040 来分配初始化为零的固定内存。

Figure 4 Allocate Memory
// allocate memory
uint GPTR = 0x0040; // Combines GMEM_FIXED and GMEM_ZEROINIT
IntPtr p = GlobalAlloc(GPTR, numBytesToEatInBytes);
if (p == IntPtr.Zero)
{
  string time = elapsedTime.Hours.ToString("00") + ":" +
         elapsedTime.Minutes.ToString("00") + ":" +
         elapsedTime.Seconds.ToString("00");
  Console.Write("{0,8}", time);
              
  Console.WriteLine("   GlobalAlloc() failed.  Trying again");
  --cycle;
  continue;
}

 

GlobalAlloc 的第二个参数是要分配的字节数。如果该分配成功,则 GlobalAlloc 返回一个指向所分配内存第一个地址的 IntPtr。如果分配失败,则返回值是 IntPtr.Zero(几乎等同于非托管代码 null)。该分配可能由于若干种原因而失败;实际上,我预料分配在某些压力情况下会失败。这样,我就不会引发一个异常,而只是打印一个警告信息,再次尝试。GlobalAlloc 函数分配来自堆的指定字节数。

处理内存分配时,需要注意三个组件:物理内存、页文件和虚拟内存。仅当指令在 RAM 中时,程序才可以执行该指令。RAM 有固定的大小;近年来,桌面计算机 RAM 的大小通常为 512MB。如果一个程序小于 RAM 的大小,则整个程序可以加载到内存中。虚拟内存的思路是允许运行大于 RAM 的程序。页文件供操作系统在必要时将代码换入内存、换出内存。默认的原始页文件大小是 RAM 大小的 1.5 倍,但页文件大小在必要时可以增加。虚拟内存的最大值是不同的,但 32 位 Windows® 版本通常为 2GB。

分配内存之后,我需要获取更新的内存状态,这样我就可以显示不同的诊断数据:

// get current memory status
GlobalMemoryStatus(ref memStatusBuffer);
uint availablePhysicalInKB =
 memStatusBuffer.dwAvailPhys / (uint)basicUnit;
uint availableVirtualInKB =
 memStatusBuffer.dwAvailVirtual / (uint)basicUnit;
numBytesToEatInBytes =
 (uint)(randomPct * memStatusBuffer.dwAvailVirtual);
numBytesToEatInKB = numBytesToEatInBytes / basicUnit;

我再次调用 GlobalMemoryStatus。这次我以千字节计算可用物理内存量,就像对虚拟内存所做的那样。然后为了进行显示,我以字节和千字节分别计算要利用的内存。

EatMem 中的多数代码行用于输出显示。我首先打印当前经过的时间:

// print time
string time = elapsedTime.Hours.ToString("00") + ":" +
       elapsedTime.Minutes.ToString("00") + ":" +
       elapsedTime.Seconds.ToString("00");
Console.Write("{0,8}", time);

然后,我打印可用的物理和虚拟内存量,以及要分配的字节数:

// print the memory numbers
Console.Write("{0,12} {1,12} {2,12}",
              availablePhysicalInKB + " KB",
              availableVirtualInKB + " KB",
              numBytesToEatInKB + " KB");

请注意,为了简化起见,我显示的已分配内存量基于当前的内存状态,而不是实际调用 GlobalAlloc 时的内存状态。接下来,我显示消耗掉的可用内存百分比,以及作为状态栏左边距的管道字符 (|):

// print the current alooc percent number
Console.Write("   " +  (int)(randomPct*100.0));
Console.Write("|");

现在,为了显示状态栏,我使用了一个古老的技巧:

// do the bars
uint totalNumBars = 20;
uint eachBarIs = availableVirtualInKB / totalNumBars;
uint barsToPrint = numBytesToEatInKB / eachBarIs;

string bars = new string('#', (int)barsToPrint);
string spaces = new string('_', 20-(int)barsToPrint);

Console.Write(bars);
Console.Write(spaces);
Console.WriteLine("|");

这里,我已经决定用 20 个字符显示状态栏。如果我有 20 个字符,则将可用虚拟内存进行 20 均分来计算每个字符表示的宽度。状态栏将包括一些字符和一些空白。字符数为已分配内存的千字节除以每个状态栏的宽度。空格数等于 20 减去字符数的结果。在前面的代码中,我使用了"#"字符。在下一节中,我将解释如何打印一个神奇的彩色栏。

所有显示工作完成后,我要将线程的执行中止一段时间,以便让我假定此时运行的测试来执行测试系统。(请注意,压力器的整体思路是建立一个计算机状态,使测试可在资源较少的情况下运行。)这里我强制暂停三秒钟,然后释放分配的内存:

// pause to give app under test stress time 
System.Threading.Thread.Sleep(3000);

// free memory
IntPtr freeResult = GlobalFree(p);
if (freeResult != IntPtr.Zero)
  throw new Exception("GlobalFree() failed");

GlobalFree 函数接受一个指针,该指针指向通过调用 GlobalAlloc 函数返回的内存。GlobalFree 尝试释放该内存。如果释放操作成功,GlobalFree 将返回 null/IntPtr.Zero。如果操作失败,GlobalFree 将返回输入参数。对于压力器而言,如果 GlobalFree 失败,则表明遇到了严重的问题,需要引发一个致命的异常。

EatMem 工具最终会在全部 8 个次循环的末尾显示信息栏,以及在当前时间超出指定的运行时间时,在主 while 循环终止以后显示一个"done"消息。

酷酷的控制台输出

编写命令行外壳测试工具时,适当使用文本颜色可以使输出更易于读取和解释。例如,EatMem 使用绿色表示内存数字,使用红色表示错误消息,使用白色空白字符创建内存分配状态栏。我的同事告诉我,我在这里使用的颜色太多了。在 Microsoft® .NET Framework 1.1 环境中打印彩色的控制台文本的技巧是:使用 Win32 函数 GetStdHndle 和 SetConsoleTextAttribute。以如下方式声明它:

[DllImport("kernel32.dll")]
extern static IntPtr GetStdHandle (int nStdHandle);

[DllImport("kernel32.dll")]
extern static bool SetConsoleTextAttribute(
    IntPtr hConsoleOutput, int wAttributes);

要设置文本的颜色,首先调用 GetStdHandle,它会将一个句柄返回到 Standard Output。然后调用 SetConsoleTextAttribute 指定文本颜色。该代码以浅黄色打印"Hello",接着是浅红色的"Bye":

const int STD_OUTPUT_HANDLE = -11;
IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

SetConsoleTextAttribute(hStdOut, 0x0006);
Console.WriteLine("Hello");
SetConsoleTextAttribute(hStdOut, 0x0004);
Console.WriteLine("Bye");

值 -11 是指定 Standard Output 的常量。常量 0x0006 和 0x0004 分别是浅黄色和浅红色。指定的文本颜色在重新调用 SetConsoleTextAtribute 之前保持不变。使用控制台输出,颜色常量的"浅"样式通常对文本太过单调。通过将其与值 0x0008 进行逻辑或 (OR) 来强化任何文本颜色。因此该代码将以亮黄色显示"Howdy Dowdy":

SetConsoleTextAttribute(hStdOut, 0x0006 | 0x0008);
Console.Write("Howdy Dowdy");

由于处理十六进制常量有点麻烦,因此通常将符号常量放到工具中。我喜欢在命令行外壳程序中使用以下一些符号常量:

const int BLACK = 0x0000;
const int BLUE = 0x0001 | 0x0008; // bright blue
const int GREEN = 0x0002 | 0x0008; // bright green
const int CYAN = 0x0003 | 0x0008; // etc.
const int RED = 0x0004 | 0x0008;
const int MAGENTA = 0x0005 | 0x0008;
const int YELLOW = 0x0006 | 0x0008;
const int WHITE = 0x0007 | 0x0008;

除了指定文本颜色,还可以指定背景颜色。为此,可将文本的颜色与另一个表示背景颜色的常量进行逻辑或 (OR)。例如,该代码将文本上下文设置为亮黄色 (0x0006 | 0x0008),背景色为浅红色(0x0040):

SetConsoleTextAttribute(hStdOut, 0x0006 | 0x0008 | 0x0040);

通常,我只喜欢使用亮白、浅白(灰色)和黑色作为背景。以下显示我在 EatMem 中使用的常量:

const int BGWHITE = 0x0070 | 0x0080;
const int BGBLACK = 0x0000 | 0x0000;
const int BGGRAY = 0x0070;

使用该代码,可以进行大量、智能的颜色格式化。下面是我在 EatMem 中打印内存状态栏的方式:

string bars = new string('_', (int)barsToPrint);
string spaces = new string('_', 20-(int)barsToPrint);
SetConsoleTextAttribute(hStdOut, BLACK | BGWHITE);
Console.Write(bars);

SetConsoleTextAttribute(hStdOut, WHITE | BGBLACK);
Console.Write(spaces);

SetConsoleTextAttribute(hStdOut, GREEN | BGBLACK);
Console.WriteLine("|");

通过在白色背景上打印黑色的空格,可生成一个白色栏。然后,如果您换成在黑色背景上打印白色文字,则生成一个黑色背景。我使用带下划线的字符在每栏下面生成一条线。

.NET Framework 2.0 中的 Console 类极大增强了对特殊输出的支持。如果在 .NET Framework 2.0 环境中工作,则使用新的 Console 类优于使用 P/Invoke 机制。Console 类包含很多方法和属性,它们用于获取和设置屏幕缓冲区、控制台窗口和光标的大小;用于更改控制台窗口和光标的位置;用于移动和清除屏幕缓冲区中的数据;用于更改前端和背景颜色;用于更改控制台标题栏中显示的文本;用于播放嘟嘟的响声。

扩展工具

我在这里展示的 EatMem 工具可按原样使用,但它是经过特殊设计的,您可以轻松地修改它。我们看一些可在扩展或增强基础版本 EatMem 时使用的方式。

最有可能修改的就是用于分配内存的技术。EatMem 使用 Win32 GlobalAlloc 和 GlobalFree 函数,但您可使用其他几个内存分配函数。一种可能是使用 Marshal.AllocHGlobal 和 Marshal.FreeHGlobal 托管方法;它们都是 LocalAlloc 和 LocalFree 的托管包装程序。AllocHGlobal 方法虽然没有 GlobalFree 灵活,但它更易于使用。但是如果您想避免直接使用 P/Invoke 机制,则可使用 AllocHGlobal。

另一个可不使用 GlobalAlloc 和 GlobalFree 的方法是使用 VirtualAlloc 和 VirtualFree Win32 函数。VirtualAlloc 函数在调用进程的虚拟地址空间中保留或提交一个页面区域。以 VirtualAlloc 为例,请参见图 5

Figure 5 Using VirtualAlloc

P/Invoke Declarations
[DllImport("kernel32.dll")]
extern static  IntPtr VirtualAlloc(IntPtr lpAddress,
  uint dwSize, uint flAllocationType, uint flProtect);
// LPVOID VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize,
//   DWORD flAllocationType, DWORD flProtect);

[DllImport("kernel32.dll")]
extern static int VirtualFree(IntPtr lpAddress,
  uint dwSize, uint dwFreeType);
// BOOL VirtualFree(LPVOID lpAddress, SIZE_T dwSize,
//   DWORD dwFreeType);


Allocating Memory
// VirtualAlloc
uint MEM_RESERVE = 0x1000;
uint MEM_COMMIT = 0x2000;
uint PAGE_READWRITE = 0x04;
IntPtr p = VirtualAlloc(IntPtr.Zero, numBytesToEatInBytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p == IntPtr.Zero)
{
  string time = elapsedTime.Hours.ToString("00") + ":" +
         elapsedTime.Minutes.ToString("00") + ":" +
         elapsedTime.Seconds.ToString("00");
  SetConsoleTextAttribute(hStdOut, CYAN | BGBLACK);
  Console.Write("{0,8}", time);
  SetConsoleTextAttribute(hStdOut, RED | BGBLACK);
  Console.WriteLine("   VirtualAlloc() failed.  Trying again");
  --cycle;
  continue;
}

Freeing Memory
uint MEM_RELEASE = 0x8000;
int freeResult = VirtualFree(p, 0, MEM_RELEASE);
if (freeResult == 0)
  throw new Exception("VirtualFree() failed !");

您可能想在基础版本的 EatMem 中添加一个增强功能,以防止自己分配内存失败。正如前面提到的,如果内存分配失败,EatMem 只打印一个错误消息并进行重试。这会导致一系列可能不被注意的失败尝试。您可以轻松地添加一个计数器,用来跟踪尝试分配连续失败的次数;而且如果该计数器超过某个阈值,您可以引发异常或动态减少所分配内存的随机最大百分比(当前硬编码为 70%)。该增强功能的一个可能变化是,跟踪失败的内存分配尝试的百分比并相应地调整压力器参数。

如果使用 .NET Framework 2.0 环境,而且由于需要 Win32 GlobalAlloc 的灵活性而不想使用托管 AllocHGlobal,则可利用新的 SafeHandle 命名空间,它包含的类提供支持文件和操作系统句柄的功能。您可以为 GlobalAlloc 创建一个从 SafeHandle 派生的类,如图 6 所示。

Figure 6 Using the SafeHandles Namespace
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
public sealed class SafeGlobalAllocHandle :
  SafeHandleZeroOrMinusOneIsInvalid
{
  [DllImport("kernel32.dll")]
  public static extern SafeGlobalAllocHandle GlobalAlloc(uint uFlags,
    IntPtr dwBytes);

  private SafeGlobalAllocHandle() : base(true) { }

  protected override bool ReleaseHandle()
  {
    return GlobalFree(handle) == IntPtr.Zero;
  }

  [SuppressUnmanagedCodeSecurity]
  [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  [DllImport("kernel32.dll", SetLastError=true)]
  private static extern IntPtr GlobalFree(IntPtr handle);
}

该方法提供了几个好处。在引发异常时使用 using 语句可更容易地清除该内存。例如:

using(SafeGlobalAllocHandle mem =
   SafeGlobalAllocHandle.GlobalAlloc(0, numBytes))
{
  if (mem.IsInvalid) { ... }
  else { ... }
}

该方法使系统更可靠,在以下情况中尤为重要,即 EatMem 压力器代码直接复制到易于出现异步失败的应用程序或测试工具中。

我的一些同事已经对休眠时间和内存分配模式进行了随机化处理。正如编写的代码,EatMem 每次通过次循环时都要休眠常量为 3000 毫秒的一段时间。在某些测试情况下,指定随机休眠时间可为您提供一个表示性更强的背景内存使用情况。与 EatMem 只要求一块内存不同的是,您能够以增量方式要求内存。该方法的要点在于维护一个保留增量分配 IntPtr 对象的 ArrayList 集合,这样您就可以通过遍历集合以增量方式按需释放内存。

除了这些相对主要的更改外,我确信您会想到很多修改,例如,参数化内存分配的随机范围,使用 GlobalMemoryStatus 函数获取附加内存状态信息,以及添加附加的错误检查。请注意,为简短起见,我决定不在本专栏中着重介绍错误处理的内容。

对它进行包装

除了使用这里提供的内存压力器之外,您也应该通过减少 CPU 利用率和磁盘空间为系统加压。这些压力器效果类似于内存缩减,但我会在将来的 Test Run 专栏中介绍其他几种技巧。

此外,您可能想研究的其他几种压力测试包括:在正常情况下,在一段较长的时间内运行测试自动化。压力分析(或者使测试的应用程序不断减少内存、CPU 可用性或磁盘空间,直到系统失败)是另一个变化。本例的思想是确定测试系统的功能边界。另一种压缩测试(特别适用于 Web 应用程序)称为"负载测试"。这通常指具有较高用户负载的 Web 应用程序和/或 Web 服务器。如您所见,在压力测试领域还有很多内容有待发现。将来的专栏中将介绍更多的内容。

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号