今天偶爾發現minimem,.net的,ildasm上
定位到emptyworkingset這個API。
查了下,早有人研究了。
轉帖過來。
EmptyWorkingSet和内存整理
2008-07-14 17:52
网络上找了很多关于内存整理的文章,不外乎都是使用EmptyWorkingSet来实现。就如下面这段代码。
#include "stdafx.h"
#include
#include
#include
#pragma comment (lib,"psapi.lib")
BOOL EmptyAllSet()
{
HANDLE SnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(SnapShot==NULL)
{
return FALSE;
}
PROCESSENTRY32 ProcessInfo;//声明进程信息变量
ProcessInfo.dwSize=sizeof(ProcessInfo);//设置ProcessInfo的大小
//返回系统中第一个进程的信息
BOOL Status=Process32First(SnapShot, &ProcessInfo);
while(Status)
{
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,TRUE,ProcessInfo.th32ProcessID);
if(hProcess)
{
SetProcessWorkingSetSize(hProcess,-1,-1);
//内存整理
EmptyWorkingSet(hProcess);
CloseHandle(hProcess);
}
//获取下一个进程的信息
Status=Process32Next(SnapShot,&ProcessInfo);
}
return TRUE;
}
int main(int argc, char* argv[])
{
EmptyAllSet();
return 0;
}
但在实际使用上,只能清理当前帐户启动的进程。其他进程,如SYSTEM帐户启动的程序,都是无法清理。这是因为权限的关系。这个时候只要加一段提升权限的代码即可。
void AdjustTokenPrivilegesForNT()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
// Get a token for this process.
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
// Get the LUID for the EmptyWorkingSet privilege.
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Get the EmptyWorkingSet privilege for this process.
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
}
然后在main的EmptyAllSet();调用之前加入AdjustTokenPrivilegesForNT();即可。这段代码与关机所用的那段很相似,有什么区别的话比较一下就知道了。
另外,很多文章说外面很多内存整理的工具不过如此,我觉得是很不负责任的。如果是使用EmptyWorkingSet实现的话当然是没什么技术含量,但是只要比较一下实现过程就会知道,很多好的内存整理工具的实现方法肯定不是这样的。运行上面这段代码耗时极短,硬盘几乎不进行读写。但是其他的整理工具往往会进行大量的硬盘读写,将内存数据转存到硬盘的分页文件里以达到整理内存的目的。孰优孰劣我不敢说,但是相对来说我还是觉得用EmptyWorkingSet实现来的快一点。
目前为止我还没有发现EmptyWorkingSet执行后会产生什么问题,如果有高人知道这个函数的缺点的话,望不吝赐教。谢谢。
http://hi.baidu.com/xuzhhua/blog/item/05dfa34ac9008ef582025cd2.html
http://webcache.googleusercontent.com/search?q=cache:jr7C5y_CDH4J:coolfire.fetag.org/%3Fp%3D334+emptyworkingset&cd=5&hl=zh-CN&ct=clnk&lr=lang_zh-TW|lang_en|lang_zh-CN&client=firefox-a記憶體回收 (EmptyWorkingSet)
11
Aug
在 LifeHacker 上看到一個叫作 Minimem 的工具,主要是用來清除系統中不用的記憶體,可以設定幾秒執行一次,也可以設定針對哪一些使用記憶體量比較大的程式進行記憶體回收的動作。下載來稍微玩了一下,發現有點用處,類似的程式有 Freemem 之類的,以前一直想瞭解這些程式的運作原理,剛好 Minimem 是 .NET Framework 3.5 的程式,所以就可以拿它來 “解剖” 一下,看看實際的工作原理是甚麼。
用 Reflector 看了一下,主要是呼叫一個叫作 EmptyWorkingSet 的 Method 來進行,而它僅是一個 Interface,調用 psapi.dll 裡的 EmptyWorkingSet 這個 Method。
[DllImport("psapi.dll")]
private static extern int EmptyWorkingSet(IntPtr hwProc);
再 Google 一下 “EmptyWorkingSet”,發現它就是利用 Windows 提供的 API 來進行記憶體回收,僅此而已,沒有其他令人驚豔的程式碼與功能,原來這僅僅是一個系統 API 而已呀。先前以為記憶體回收是一個怎麼樣困難的技術,現在發現原來困難的技術已經被包成系統 API (“某些”系統,就是需要這些 API),很多的工具都可以利用呼叫這個 API 來達到回收效果,下回自己寫的程式吃太多記憶體的話,可以試一下寫一個會自我回收記憶體的程式。
[Reference]
http://msdn.microsoft.com/en-us/library/ms682606.aspx
http://www.google.com.hk/codesearch?q=EmptyWorkingSet&ie=UTF-8&oe=utf-8&rls=org.mozilla:zh-TW:official&client=firefox&um=1&sa=X&oi=codesearch_group&resnum=4&ct=title
至於 Minimem 嘛,仍有它的功能在,作為一個設定固定時間、針對 Process 記憶體使用量執行的介面程式,不過我還是覺得它應該要寫成 Windows 的 Service 比較好。
[Update 2008-09-24]
出差在飯店晚上無聊的時候弄了一個 Service 的版本,暫時叫作 More Memory V1.0,可以在下面的連結下載:
http://groups.google.com/group/Autorun-DIY/web/MoreMem100.zip?hl=zh-TW
安裝很簡單,就是建一個目錄,把檔案都放進去,然後跑一下 Install.bat,接著到[控制台][系統管理工具][服務] 裡頭就可以看到 FETAG – More Memory Service 把它設為 “啟動” 就可以了。要移除的話就執行 UnUnstall.bat 即可。
api說明:
http://msdn.microsoft.com/en-us/library/ms682606(VS.85).aspx
老外搞了AHK,果然萬能的AHK..
http://www.autohotkey.com/forum/topic53543.html
代碼複製學習
; =============== Auto-execute ===============
#NoEnv
SendMode, Input
SetWorkingDir, %A_ScriptDir%
SetTitleMatchMode, 3
DetectHiddenWindows, On
IfExist, ram.ico
Menu, Tray, Icon, ram.ico
Menu, Tray, NoStandard
Menu, Tray, Add, Show Window, ShowWindow
Menu, Tray, Add, Exit, GuiClose
Menu, Tray, Default, Show Window
; =============== Variables ===============
Version = 1.1.0
; =============== GUI ===============
GoSub, ReadINI
Gui, Add, Button, x15 y395 w80 h30, Start
Gui, Add, Button, x175 y395 w70 h30, Hide
Gui, Add, Button, x255 y395 w70 h30, Exit
Gui, Add, Tab, x0 y5 w340 h385, Status|Settings|About
Gui, Tab, Status
Gui, Add, Edit, x20 y60 w300 h300 +ReadOnly -Wrap
Gui, Tab, Settings
Gui, Font, S12 Bold,
Gui, Add, Text, x130 y30, Interval
Gui, Add, Text, x135 y90, Type
Gui, Add, Text, x130 y280, Startup
Gui, Font,,
Gui, Add, Edit, x110 y60 w100 h20 vRunInterval, %RunInterval%
Gui, Add, Text, x+10 y60 w100 h30, min
Gui, Add, Radio, vRunType x10 y120, All processes (Exclusions)
Gui, Add, Radio, x170 y120, Specific process (Inclusions)
Gui, Add, Edit, vExclusions x15 y150 w150 h100 -Wrap, %Exclusions%
Gui, Add, Edit, vInclusions x175 y150 w150 h100 -Wrap, %Inclusions%
Gui, Add, Checkbox, vAutorun Checked%Autorun% x90 y310, Auto-run
Gui, Add, Checkbox, vHide Checked%Hide% x180 y310, Start hidden
Gui, Add, Button, x120 y355, Save Settings
Gui, Tab, About
Gui, Font, S14
Gui, Add, Text, x36 y100 w250 h150 , Written by:`n`nMatthew Giffin`nstrictlyfocused02@gmail.com
Gui, Font,,
Gui, Show, h430 w340, Memory Cleanup v%Version%
If RunType = 1
Control, Check,, Button4, Memory Cleanup v%Version%
If RunType = 2
Control, Check,, Button5, Memory Cleanup v%Version%
If Hide = 1
GoSub, ButtonHide
If Autorun = 1
GoSub, ButtonStart
Return
; =============== GUI Sub-routines ===============
ButtonExit:
GuiClose:
ExitApp
GuiSize:
If A_EventInfo = 1
WinHide, Memory Cleanup v%Version%
Return
ButtonHide:
WinHide, Memory Cleanup v%Version%
Return
ShowWindow:
WinShow, Memory Cleanup v%Version%
WinWait, Memory Cleanup v%Version%
WinActivate, Memory Cleanup v%Version%
Return
ButtonStart:
Gui, Submit, NoHide
GoSub, MemoryCleanup
GoSub, SetInterval
Return
SetInterval:
AHKRunInterval := RunInterval * 60000
SetTimer, MemoryCleanup, %AHKRunInterval%
Return
ReadINI:
IfExist, settings.ini
{
IniRead, RunInterval, settings.ini, Interval, Interval
IniRead, RunType, settings.ini, Type, RunType
IniRead, Exclusions, settings.ini, Type, Exclusions
IniRead, Inclusions, settings.ini, Type, Inclusions
IniRead, Autorun, settings.ini, Launch, Autorun
IniRead, Hide, settings.ini, Launch, Hide
If not Exclusions
Exclusions = One process name per line
If Exclusions
StringReplace, Exclusions, Exclusions, `,, `n, All
If not Inclusions
Inclusions = One process name per line
If Inclusions
StringReplace, Inclusions, Inclusions, `,, `n, All
Return
}
IfNotExist, settings.ini
{
RunInterval = 30
RunType = 1
Exclusions = One process name per line
Inclusions = One process name per line
Autorun = 0
Hide = 0
Return
}
ButtonSaveSettings:
Gui, Submit, NoHide
If Exclusions = One process name per line
Exclusions =
If Inclusions = One process name per line
Inclusions =
StringReplace, Exclusions, Exclusions, `n, `,, All
StringReplace, Inclusions, Inclusions, `n, `,, All
IniWrite, %RunInterval%, settings.ini, Interval, Interval
IniWrite, %RunType%, settings.ini, Type, RunType
IniWrite, %Exclusions%, settings.ini, Type, Exclusions
IniWrite, %Inclusions%, settings.ini, Type, Inclusions
IniWrite, %Autorun%, settings.ini, Launch, Autorun
IniWrite, %Hide%, settings.ini, Launch, Hide
MsgBox, Settings saved!`n`nTip: Press "Start" now to apply these settings without having to restart the application.
Return
; =============== Bread and Butter ===============
MemoryCleanup:
ProcList := CMDret("tasklist /fo CSV") ;Send "tasklist" to cmdret
Loop, Parse, ProcList, `n
{
If A_Index < 4 ;The first 4 lines are always junk\column|row seperators\blank
Continue
StringSplit, ProcArray, A_LoopField, `, ;Split apart using a comma delimeter
StringReplace, ProcArray1, ProcArray1, `",, 1 ;Process Name
StringReplace, ProcArray2, ProcArray2, `",, 1 ;PID
If RunType = 1
{
StringReplace, Exclusions, Exclusions, `n, `,, All
If ProcArray1 in %Exclusions%
Continue
Else
{
GO := CleanMemory(ProcArray2)
If GO = 1 ;Function returned 1 (i.e. success)
{
ControlGetText, OnScrLog, Edit1, Memory Cleanup v%Version%
ControlSetText, Edit1, %OnScrLog%`r`n`r`n%A_MMM%-%A_DD%-%A_YYYY% %A_Hour%:%A_Min%:%A_Sec%:%A_MSec% - Cleaned "%ProcArray1%", Memory Cleanup v%Version%
SendMessage, 0x115, 7, 0, Edit1, Memory Cleanup v%Version%
}
}
}
If RunType = 2
{
StringReplace, Inclusions, Inclusions, `n, `,, All
If ProcArray1 in %Inclusions%
{
GO := CleanMemory(ProcArray2)
If GO = 1 ;Function returned 1 (i.e. success)
{
ControlGetText, OnScrLog, Edit1, Memory Cleanup v%Version%
ControlSetText, Edit1, %OnScrLog%`r`n`r`n%A_MMM%-%A_DD%-%A_YYYY% %A_Hour%:%A_Min%:%A_Sec%:%A_MSec% - Cleaned "%ProcArray1%", Memory Cleanup v%Version%
SendMessage, 0x115, 7, 0, Edit1, Memory Cleanup v%Version%
}
}
}
}
Return
; =============== Functions ===============
;A function that retreives the process handle from a PID and sends it to the EmptyWorkingSet API. Returns 1 for success, NULL (blank) on failure
CleanMemory(PID) ;Written with help from "Temp01" on the AHK IRC chat (thank you again, temp01!!!)
{
Process, Exist ;Sets ErrorLevel to the PID of this running script
h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel) ;Get the handle of this script with PROCESS_QUERY_INFORMATION (0x0400)
DllCall("Advapi32.dll\OpenProcessToken", "UInt", h, "UInt", 32, "UIntP", t) ;Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
VarSetCapacity(ti, 16, 0) ;Structure of privileges
NumPut(1, ti, 0) ;One entry in the privileges array...
DllCall("Advapi32.dll\LookupPrivilegeValueA", "UInt", 0, "Str", "SeDebugPrivilege", "Int64P", luid) ;Retrieves the locally unique identifier of the debug privilege:
NumPut(luid, ti, 4, "int64")
NumPut(2, ti, 12) ;Enable this privilege: SE_PRIVILEGE_ENABLED = 2
DllCall("Advapi32.dll\AdjustTokenPrivileges", "UInt", t, "Int", false, "UInt", &ti, "UInt", 0, "UInt", 0, "UInt", 0) ;Update the privileges of this process with the new access token:
DllCall("CloseHandle", "UInt", h) ;Close this process handle to save memory
hModule := DllCall("LoadLibrary", "Str", "Psapi.dll") ;Increase performance by preloading the libaray
h := DllCall("OpenProcess", "UInt", 0x400|0x100, "Int", false, "UInt", pid) ;Open process with: PROCESS_QUERY_INFORMATION (0x0400) | PROCESS_SET_QUOTA (0x100)
e := DllCall("psapi.dll\EmptyWorkingSet", "UInt", h)
DllCall("CloseHandle", "UInt", h) ;Close process handle to save memory
DllCall("FreeLibrary", "UInt", hModule) ;Unload the library to free memory
Return e
}
;CMDret can be used to retrieve and store output from console programs in a variable without displaying the console window
CMDret(CMDin, WorkingDir=0) ;by corrupt (http://www.autohotkey.com/forum/topic8606.html)
{
Global cmdretPID
tcWrk := WorkingDir=0 ? "Int" : "Str"
idltm := A_TickCount + 20
CMsize = 1
VarSetCapacity(CMDout, 1, 32)
VarSetCapacity(sui,68, 0)
VarSetCapacity(pi, 16, 0)
VarSetCapacity(pa, 12, 0)
Loop, 4 {
DllCall("RtlFillMemory", UInt,&pa+A_Index-1, UInt,1, UChar,12 >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&pa+8+A_Index-1, UInt,1, UChar,1 >> 8*A_Index-8)
}
IF (DllCall("CreatePipe", "UInt*",hRead, "UInt*",hWrite, "UInt",&pa, "Int",0) <> 0) {
Loop, 4
DllCall("RtlFillMemory", UInt,&sui+A_Index-1, UInt,1, UChar,68 >> 8*A_Index-8)
DllCall("GetStartupInfo", "UInt", &sui)
Loop, 4 {
DllCall("RtlFillMemory", UInt,&sui+44+A_Index-1, UInt,1, UChar,257 >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+60+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+64+A_Index-1, UInt,1, UChar,hWrite >> 8*A_Index-8)
DllCall("RtlFillMemory", UInt,&sui+48+A_Index-1, UInt,1, UChar,0 >> 8*A_Index-8)
}
IF (DllCall("CreateProcess", Int,0, Str,CMDin, Int,0, Int,0, Int,1, "UInt",0, Int,0, tcWrk, WorkingDir, UInt,&sui, UInt,&pi) <> 0) {
Loop, 4
cmdretPID += *(&pi+8+A_Index-1) << 8*A_Index-8
Loop {
idltm2 := A_TickCount - idltm
If (idltm2 < 10) {
DllCall("Sleep", Int, 10)
Continue
}
IF (DllCall("PeekNamedPipe", "uint", hRead, "uint", 0, "uint", 0, "uint", 0, "uint*", bSize, "uint", 0 ) <> 0 ) {
Process, Exist, %cmdretPID%
IF (ErrorLevel OR bSize > 0) {
IF (bSize > 0) {
VarSetCapacity(lpBuffer, bSize+1)
IF (DllCall("ReadFile", "UInt",hRead, "Str", lpBuffer, "Int",bSize, "UInt*",bRead, "Int",0) > 0) {
IF (bRead > 0) {
TRead += bRead
VarSetCapacity(CMcpy, (bRead+CMsize+1), 0)
CMcpy = a
DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", &CMDout, "Int", CMsize)
DllCall("RtlMoveMemory", "UInt", &CMcpy+CMsize, "UInt", &lpBuffer, "Int", bRead)
CMsize += bRead
VarSetCapacity(CMDout, (CMsize + 1), 0)
CMDout=a
DllCall("RtlMoveMemory", "UInt", &CMDout, "UInt", &CMcpy, "Int", CMsize)
VarSetCapacity(CMDout, -1) ; fix required by change in autohotkey v1.0.44.14
}
}
}
}
ELSE
break
}
ELSE
break
idltm := A_TickCount
}
cmdretPID=
DllCall("CloseHandle", UInt, hWrite)
DllCall("CloseHandle", UInt, hRead)
}
}
IF (StrLen(CMDout) < TRead) {
VarSetCapacity(CMcpy, TRead, 32)
TRead2 = %TRead%
Loop {
DllCall("RtlZeroMemory", "UInt", &CMcpy, Int, TRead)
NULLptr := StrLen(CMDout)
cpsize := Tread - NULLptr
DllCall("RtlMoveMemory", "UInt", &CMcpy, "UInt", (&CMDout + NULLptr + 2), "Int", (cpsize - 1))
DllCall("RtlZeroMemory", "UInt", (&CMDout + NULLptr), Int, cpsize)
DllCall("RtlMoveMemory", "UInt", (&CMDout + NULLptr), "UInt", &CMcpy, "Int", cpsize)
TRead2 --
IF (StrLen(CMDout) > TRead2)
break
}
}
StringTrimLeft, CMDout, CMDout, 1
Return, CMDout
}
/*
>>>>CHANGELOG<<<<
1.0 (12-23-2009)
- Original script
1.1 (10-24-2009)
- Removed need for empty.exe
-- Added CleanMemory function which uses the same API (EmptyWorkingSet) found in empty.exe
沒有留言:
發佈留言