C技巧辅导(内存分配:更换策略,不要为难内存)
来源:优易学  2011-11-18 13:13:04   【优易学:中国教育考试门户网】   资料下载   IT书店
  在32位机上(64位也是一样的,但是空间大很多),一个进程可以分配到4GB的虚拟内存,当然,其中2G给了内核,剩下2GB有一些分给了代码段、数据段,最后剩下的就是给我们程序员用的了,这样看来,一个应用程序若硬生生的读取2GB左右的数据是一个极限了。
  不过事实并非如此,只要利用虚拟内存技术有时还是可以读取的。
  先简单说说windows虚拟内存的思路:
  所谓虚拟内存,就是事实上可能还没有得到实际内存的内存,如果CPU要访问一个虚拟内存,哪么操作系统得首先判断这个虚拟内存有没有获得实际内存。如果有,那大可以大大方方的读取,甚至写数据。
  但若如果没有,操作系统就必须在实际内存上找到一块地方,建立起与要读取虚拟内存的映射关系,这样就可以读取了。当然若是找不到这么多实际内存,那么可以一些暂时还没用到的实际内存放到更低一级的硬盘临时空间上,腾出空间来,再建立与虚拟内存的映射关系。
  当然了,比如要读取2GB的数据,在1G内存上是无论如何也没法找到足够空间建立映射关系的。我们当然不可能一次性读取2GB数据,所以引入“页”这个概念,即每次虚拟内存射到实际内存上的时候,都是按页大小映射的,这个页大小,与CPU有关,在X86上一般是4KB。
  说到这,读取大型数据的思路就出来了,我们可以先分配虚拟内存,等到我们读或者写当中的一些数据的时候,再分配实际内存,由于这些都是在内存这一级操作,效率远比读写数据的时候再从硬盘取出来的高。
  windows API让我们轻松做到这一点。
  VirtualAlloc,可以申请到一个虚拟内存,或者实际映射到实际内存的内存。我们可以先申请虚拟内存,等到真的要读取某处数据的时候,再申请实际内存(建立映射关系)。
  详细可以看MSDN,下面给出一个示例代码:
  #include <Windows.h>
  #include <string>
  #include <iostream>
  struct Sheet //我们要读取的数据结构
  {
  int nSize;
  BOOL bVisible;
  char big[1024 * 15];
  std::string strText;
  };
  int main()
  {
  SYSTEM_INFO si;
  GetSystemInfo(&si);
  DWORD dwPageSize = si.dwPageSize; //获得CPU读取的页大小
  //把页的单位转为字节(页的单位本来是KB,而我们的数据结构是按字节算的)
  DWORD dwPageAsByte = dwPageSize * 1024;
  //以下dwOccupy获得一个数据结构所占的空间,由于是按页分配实际内存,这样所占空间并定是页的整数倍
  DWORD dwMod = sizeof(Sheet) % dwPageAsByte;
  DWORD dwOccupy = 0;
  if (dwMod != 0)
  {
  dwOccupy = (sizeof(Sheet) / dwPageAsByte) * dwPageAsByte + dwPageAsByte;
  }
  else
  {
  dwOccupy = sizeof(Sheet);
  }
  //申请虚拟内存,MEM_RESERVE表示不会和实际内存建立映射关系
  LPVOID lpBaseAddr = VirtualAlloc(NULL, dwOccupy * 50, MEM_RESERVE, PAGE_READWRITE);
  if (lpBaseAddr == NULL)
  {
  return 1;
  }
  __try
  {
  DWORD dwIndex = 0;
  //以下不断的输入要读取哪个数据,若此数据还有没有建立实际内存映射关系,那么建立
  while(true)
  {
  std::cout << "input the sheet number to write: ";
  std::cin >> dwIndex;
  //根据输入的索引,计算出所要读取的数据结构所在地址,注意同样是按页分配的
  LPVOID lpSheetAddr = (LPVOID)((DWORD)lpBaseAddr + (dwIndex - 1) * dwOccupy);
  MEMORY_BASIC_INFORMATION mbi;
  memset(&mbi, 0, sizeof(mbi));
  //查询内存状态
  if (VirtualQuery(lpSheetAddr, &mbi, sizeof(mbi)) == sizeof(mbi))
  {
  if (mbi.State == MEM_RESERVE)//属于reserve状态
  {
  //建立与实际内存的映射关系
  LPVOID lpAlloc = VirtualAlloc(lpSheetAddr, dwOccupy, MEM_COMMIT, PAGE_READWRITE);
  if (lpAlloc == NULL)
  {
  std::cout << "commit fails in reserve state\n";
  }
  else
  {
  std::cout << "commit successes in reserve state\n";
  }
  }
  }
  //建立成功,青年人网提示读/写数据
  Sheet * pSheet = (Sheet*)lpSheetAddr;
  pSheet->nSize = 9;
  pSheet->strText = "hello world";
  pSheet->bVisible = TRUE;
  }
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
  }
  //释放内存
  VirtualFree(lpBaseAddr, 0, MEM_RELEASE);
  }
  当然,这其中用到了一个影响效率的VirtualQuery函数,其实可以用异常处理手段提高效率的,留待以后再说。
  还可以添加删除代码,一些数据不再用到的时候,可以把虚拟内存状态重新置为MEM_RESERVE,这样可以节省实际内存。

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训