获取虚拟分配地址的更快方法
Faster way to get virtual allocation addresses
我想处理(保存)分配给当前进程的虚拟内存块。这是我使用的代码:
program Project38;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.Windows;
procedure DoProcess(aStart: Pointer; aSize: Cardinal);
begin
// process it
end;
procedure ProcessVirtualMemory;
var
addr: Pointer;
i: Integer;
p: Pointer;
systemInfo: SYSTEM_INFO;
startAddress, stopAddress: Pointer;
size: size_t;
memInfo: MEMORY_BASIC_INFORMATION;
begin
GetSystemInfo(systemInfo);
startAddress := systemInfo.lpMinimumApplicationAddress;
stopAddress := systemInfo.lpMaximumApplicationAddress;
addr := startAddress;
while NativeUInt(addr) < NativeUInt(stopAddress) do begin
size:= VirtualQuery(Pointer(addr), memInfo, SizeOf(MEMORY_BASIC_INFORMATION));
if (size = SizeOf(MEMORY_BASIC_INFORMATION)) and
(memInfo.State = MEM_COMMIT) and
(memInfo.Type_9 = MEM_PRIVATE) and
(memInfo.RegionSize > 0) and
(memInfo.Protect = PAGE_READWRITE) then
begin
DoProcess(memInfo.BaseAddress, memInfo.RegionSize);
addr := Pointer(NativeUInt(addr) + memInfo.RegionSize);
end
else
addr := Pointer(NativeUInt(addr) + systemInfo.dwPageSize);
end;
end;
begin
ProcessVirtualMemory;
end.
此代码 运行 具有庞大的应用程序,收集此信息而不进行处理需要 10-12 秒。有没有更快的方法获取虚拟内存块的地址?
你的程序确实有错误。如果内存块与您的搜索条件不匹配,您只会增加页面大小而不是区域大小。你的循环应该看起来像这样:
while NativeUInt(addr) < NativeUInt(stopAddress) do begin
size := VirtualQuery(Pointer(addr), memInfo, SizeOf(MEMORY_BASIC_INFORMATION));
if size = 0 then begin
// handle error
break
end;
if (size = SizeOf(MEMORY_BASIC_INFORMATION)) and
(memInfo.State = MEM_COMMIT) and
(memInfo.Type_9 = MEM_PRIVATE) and
(memInfo.RegionSize > 0) and
(memInfo.Protect = PAGE_READWRITE) then begin
DoProcess(memInfo.BaseAddress, memInfo.RegionSize);
end;
addr := Pointer(NativeUInt(addr) + memInfo.RegionSize);
end;
您的版本的问题在于,当虚拟地址 space 中存在间隙时,您的代码会一次一页地遍历它们,而不是跳过整个区域。
即使没有那个改变,我也不认为枚举是瓶颈。我对您的原始程序进行了以下更改,以报告所花费的时间:
var
Stopwatch: TStopwatch;
begin
Stopwatch := TStopwatch.StartNew;
ProcessVirtualMemory;
Writeln(Stopwatch.ElapsedMilliseconds);
Readln;
end.
在我的机器上,对于 32 位版本构建,这报告了大约 500 毫秒。
然后我分配了一些内存:
var
i: Integer;
Stopwatch: TStopwatch;
begin
for i := 1 to 100000 do begin
HeapAlloc(GetProcessHeap, 0, Random(100000));
end;
Stopwatch := TStopwatch.StartNew;
ProcessVirtualMemory;
Writeln(Stopwatch.ElapsedMilliseconds);
Readln;
end.
无论我做什么,摆弄常量,程序仍然报告大约 500 毫秒。如果您按照本答案开头所述修复不匹配的增量代码,则时间会下降到 100 毫秒左右。
当然,对于 64 位进程,情况略有不同。您的代码中的缺陷意味着该程序有效地卡住了,一次遍历巨大的 64 位地址 space 一页,为地址 space 中的每一页调用 VirtualQuery
。我什至没有等到那个过程完成。
因此,我的结论是您程序中的主要瓶颈不是您当前的代码,即查找虚拟内存块的代码。那是 DoProcess
中的代码,您拥有我们没有的代码。因此,即使您修复了我在开始时描述的缺陷,您仍然会在该功能上花费大量时间。您应该期望虚拟内存 space 枚举在 100 毫秒左右。
我想处理(保存)分配给当前进程的虚拟内存块。这是我使用的代码:
program Project38;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.Windows;
procedure DoProcess(aStart: Pointer; aSize: Cardinal);
begin
// process it
end;
procedure ProcessVirtualMemory;
var
addr: Pointer;
i: Integer;
p: Pointer;
systemInfo: SYSTEM_INFO;
startAddress, stopAddress: Pointer;
size: size_t;
memInfo: MEMORY_BASIC_INFORMATION;
begin
GetSystemInfo(systemInfo);
startAddress := systemInfo.lpMinimumApplicationAddress;
stopAddress := systemInfo.lpMaximumApplicationAddress;
addr := startAddress;
while NativeUInt(addr) < NativeUInt(stopAddress) do begin
size:= VirtualQuery(Pointer(addr), memInfo, SizeOf(MEMORY_BASIC_INFORMATION));
if (size = SizeOf(MEMORY_BASIC_INFORMATION)) and
(memInfo.State = MEM_COMMIT) and
(memInfo.Type_9 = MEM_PRIVATE) and
(memInfo.RegionSize > 0) and
(memInfo.Protect = PAGE_READWRITE) then
begin
DoProcess(memInfo.BaseAddress, memInfo.RegionSize);
addr := Pointer(NativeUInt(addr) + memInfo.RegionSize);
end
else
addr := Pointer(NativeUInt(addr) + systemInfo.dwPageSize);
end;
end;
begin
ProcessVirtualMemory;
end.
此代码 运行 具有庞大的应用程序,收集此信息而不进行处理需要 10-12 秒。有没有更快的方法获取虚拟内存块的地址?
你的程序确实有错误。如果内存块与您的搜索条件不匹配,您只会增加页面大小而不是区域大小。你的循环应该看起来像这样:
while NativeUInt(addr) < NativeUInt(stopAddress) do begin
size := VirtualQuery(Pointer(addr), memInfo, SizeOf(MEMORY_BASIC_INFORMATION));
if size = 0 then begin
// handle error
break
end;
if (size = SizeOf(MEMORY_BASIC_INFORMATION)) and
(memInfo.State = MEM_COMMIT) and
(memInfo.Type_9 = MEM_PRIVATE) and
(memInfo.RegionSize > 0) and
(memInfo.Protect = PAGE_READWRITE) then begin
DoProcess(memInfo.BaseAddress, memInfo.RegionSize);
end;
addr := Pointer(NativeUInt(addr) + memInfo.RegionSize);
end;
您的版本的问题在于,当虚拟地址 space 中存在间隙时,您的代码会一次一页地遍历它们,而不是跳过整个区域。
即使没有那个改变,我也不认为枚举是瓶颈。我对您的原始程序进行了以下更改,以报告所花费的时间:
var
Stopwatch: TStopwatch;
begin
Stopwatch := TStopwatch.StartNew;
ProcessVirtualMemory;
Writeln(Stopwatch.ElapsedMilliseconds);
Readln;
end.
在我的机器上,对于 32 位版本构建,这报告了大约 500 毫秒。
然后我分配了一些内存:
var
i: Integer;
Stopwatch: TStopwatch;
begin
for i := 1 to 100000 do begin
HeapAlloc(GetProcessHeap, 0, Random(100000));
end;
Stopwatch := TStopwatch.StartNew;
ProcessVirtualMemory;
Writeln(Stopwatch.ElapsedMilliseconds);
Readln;
end.
无论我做什么,摆弄常量,程序仍然报告大约 500 毫秒。如果您按照本答案开头所述修复不匹配的增量代码,则时间会下降到 100 毫秒左右。
当然,对于 64 位进程,情况略有不同。您的代码中的缺陷意味着该程序有效地卡住了,一次遍历巨大的 64 位地址 space 一页,为地址 space 中的每一页调用 VirtualQuery
。我什至没有等到那个过程完成。
因此,我的结论是您程序中的主要瓶颈不是您当前的代码,即查找虚拟内存块的代码。那是 DoProcess
中的代码,您拥有我们没有的代码。因此,即使您修复了我在开始时描述的缺陷,您仍然会在该功能上花费大量时间。您应该期望虚拟内存 space 枚举在 100 毫秒左右。