关闭

[置顶] Windows内存清理工具实现——从现象到本质

标签: 内存清理工具Windows内存从现象到本质
4373人阅读 评论(3) 收藏 举报
分类:

目前,有很多Windows清理内存的工具,如Wise Memory Optimizer、 MemoryZipperPlus、SweepRAM等,360安全卫士、腾讯电脑管家、鲁大师等等系统工具也带有清理内存的功能,这类工具数不胜数。


这些工具主要使用的是Windows提供的API:EmptyWorkingSet 或SetProcessWorkingSetSize 进行所谓的内存清理。


EmptyWorkingSet 强制将进程工作集中的内存尽可能多地移动到页面文件中 (Removes as many pages as possible from the working set of the specified process.),函数原型为:

BOOL WINAPI EmptyWorkingSet(
  _In_ HANDLE hProcess
);


SetProcessWorkingSetSize可以设置进程工作集中内存的最大最小值(Sets the minimum and maximum working set sizes for the specified process.),函数原型为:

BOOL WINAPI SetProcessWorkingSetSize(
  _In_ HANDLE hProcess,
  _In_ SIZE_T dwMinimumWorkingSetSize,
  _In_ SIZE_T dwMaximumWorkingSetSize
);
其中,如果后两个参数均为-1时,该函数效果与EmptyWorkingSet相同(The working set of the specified process can be emptied by specifying the value (SIZE_T)–1 for both the minimum and maximum working set sizes. This removes as many pages as possible from the working set. The EmptyWorkingSet function can also be used for this purpose.)。


使用上面介绍的API,我们就可以实现自己的内存清理工具:


第一步,提升程序进程权限,两个API函数均需要传入要清理的线程句柄,该句柄可以通过OpenProcess得到,而如果进程权限不够,将导致打开进程失败。

准确地说,不是提升权限,而是把令牌中禁用的权限启用。

MSDN上指出,如果需要打开其他进程并获得所有权限,需要启用SeDebugPrivilege权限(To open a handle to another process and obtain full access rights, you must enable the SeDebugPrivilege privilege)。

启用SeDebugPrivilege权限,需要用到的API有:OpenProcessTokenLookupPrivilegeValueAdjustTokenPrivilegesCloseHandle,以上API用法MSDN上有详细的讲解(Enabling and Disabling Privileges in C++),下面给出相关代码:

BOOL EnableDebugPrivilege()
{
	BOOL bRet = FALSE;
	HANDLE hToken;
	if (::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		LUID luid;
		if (::LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid))
		{
			TOKEN_PRIVILEGES tp;
			tp.PrivilegeCount = 1UL;
			tp.Privileges[0].Luid = luid;
			tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
			if (::AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
			{
				bRet = TRUE;
			}
		}
		::CloseHandle(hToken);
	}
	return bRet;
}

第二步,遍历系统所有进程,调用上面两个函数传入的只是一个进程的句柄,也就是说每次调用只针对于一个进程的内存空间,所以我们还需要遍历整个系统的所有进程,然后获取每个进程的句柄,将该句柄当做参数传入上面两个函数。遍历系统所有进程有很多方法,这里使用ToolHelp API获取。其中用到3个API函数:CreateToolhelp32SnapshotProcess32FirstProcess32Next,以上API用法MSDN上已有详细介绍与例子(Taking a Snapshot and Viewing Processes),这里也不再一一介绍。


第三步,在上面遍历过程中调用EmptyWorkingSet 或 SetProcessWorkingSetSize 来实现“清理内存”。下面给出实现代码,由于EmptyWorkingSet需要包含Psapi.h头文件,代码中使用的是SetProcessWorkingSetSize函数

BOOL EmptyAllProcess()
{
	BOOL bRet = FALSE;
	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0UL);
	if (hProcessSnap != INVALID_HANDLE_VALUE)
	{
		PROCESSENTRY32 pe32;
		pe32.dwSize = sizeof(PROCESSENTRY32);
		if (::Process32First(hProcessSnap, &pe32))
		{
			do
			{
				HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
				if (hProcess)
				{
					::SetProcessWorkingSetSize(hProcess, (SIZE_T)-1, (SIZE_T)-1);
					::CloseHandle(hProcess);
				}
			} while (::Process32Next(hProcessSnap, &pe32));
			bRet = TRUE;
		}
		::CloseHandle(hProcessSnap);
	}
	return bRet;
}


第四步,为了更加直观的显示优化后所腾出的内存空间,可以在清理前后获取系统内存使用状态。这里用到GlobalMemoryStatusEx函数来获取当前系统物理内存与虚拟内存。GlobalMemoryStatus函数也可以获取,但在内存超过4GB时得到的结果不正确(On computers with more than 4 GB of memory, the GlobalMemoryStatus function can return incorrect information, reporting a value of –1 to indicate an overflow. )。显示内存使用状况代码参考于MSDN上例子,这里使用宏以便于控制内存大小输出时的单位,代码实现如下:

#define MB_UNIT

#if defined KB_UNIT

#define DIV	(1024ULL)
#define WIDTH	10

#elif defined MB_UNIT

#define DIV	(1024ULL * 1024ULL)
#define WIDTH	7

#elif defined GB_UNIT

#define DIV	(1024ULL * 1024ULL * 1024ULL)
#define WIDTH	4

#endif

void PrintMemoryStatus()
{
	MEMORYSTATUSEX statex;
	statex.dwLength = sizeof(statex);
	if (::GlobalMemoryStatusEx(&statex))
	{
#if defined KB_UNIT
		_tprintf(_T("There is  %*ld percent of memory in use.\n"), WIDTH, statex.dwMemoryLoad);
		_tprintf(_T("There are %*I64d total KB of physical memory.\n"), WIDTH, statex.ullTotalPhys / DIV);
		_tprintf(_T("There are %*I64d free  KB of physical memory.\n"), WIDTH, statex.ullAvailPhys / DIV);
		_tprintf(_T("There are %*I64d total KB of paging file.\n"), WIDTH, statex.ullTotalPageFile / DIV);
		_tprintf(_T("There are %*I64d free  KB of paging file.\n"), WIDTH, statex.ullAvailPageFile / DIV);
		_tprintf(_T("There are %*I64d total KB of virtual memory.\n"), WIDTH, statex.ullTotalVirtual / DIV);
		_tprintf(_T("There are %*I64d free  KB of virtual memory.\n"), WIDTH, statex.ullAvailVirtual / DIV);
		//_tprintf(_T("There are %*I64d free  KB of extended memory.\n"), WIDTH, statex.ullAvailExtendedVirtual / DIV);
#elif defined MB_UNIT
		_tprintf(_T("There is  %*ld percent of memory in use.\n"), WIDTH, statex.dwMemoryLoad);
		_tprintf(_T("There are %*I64d total MB of physical memory.\n"), WIDTH, statex.ullTotalPhys / DIV);
		_tprintf(_T("There are %*I64d free  MB of physical memory.\n"), WIDTH, statex.ullAvailPhys / DIV);
		_tprintf(_T("There are %*I64d total MB of paging file.\n"), WIDTH, statex.ullTotalPageFile / DIV);
		_tprintf(_T("There are %*I64d free  MB of paging file.\n"), WIDTH, statex.ullAvailPageFile / DIV);
		_tprintf(_T("There are %*I64d total MB of virtual memory.\n"), WIDTH, statex.ullTotalVirtual / DIV);
		_tprintf(_T("There are %*I64d free  MB of virtual memory.\n"), WIDTH, statex.ullAvailVirtual / DIV);
		//_tprintf(_T("There are %*I64d free  MB of extended memory.\n"), WIDTH, statex.ullAvailExtendedVirtual / DIV);
#elif defined GB_UNIT
		_tprintf(_T("There is  %*ld percent of memory in use.\n"), WIDTH, statex.dwMemoryLoad);
		_tprintf(_T("There are %*I64d total GB of physical memory.\n"), WIDTH, statex.ullTotalPhys / DIV);
		_tprintf(_T("There are %*I64d free  GB of physical memory.\n"), WIDTH, statex.ullAvailPhys / DIV);
		_tprintf(_T("There are %*I64d total GB of paging file.\n"), WIDTH, statex.ullTotalPageFile / DIV);
		_tprintf(_T("There are %*I64d free  GB of paging file.\n"), WIDTH, statex.ullAvailPageFile / DIV);
		_tprintf(_T("There are %*I64d total GB of virtual memory.\n"), WIDTH, statex.ullTotalVirtual / DIV);
		_tprintf(_T("There are %*I64d free  GB of virtual memory.\n"), WIDTH, statex.ullAvailVirtual / DIV);
		//_tprintf(_T("There are %*I64d free  GB of extended memory.\n"), WIDTH, statex.ullAvailExtendedVirtual / DIV);
#endif
		_tprintf(_T("\n"));
	}
	else
	{
		_tprintf(_T("GlobalMemoryStatusEx Failed!\n\n"));
	}
}


程序为了编写简便,创建为控制台工程,编译链接后运行效果截图如下:



程序及完整源代码下载链接点击打开链接



//------------------------------------------------------------华丽的分割线-----------------------------------------------------------------------------------------------------//



看了上面介绍,相信大部分人会认为这是一个非常有效的方法,因为使用后确实能够腾出大量内存空间,明显减小内存的占用率。可是,你看了下文就不得不承认,这种伎俩其实只是一种假象,所有的努力可能只会让事情变得更加糟糕!不信继续往下看。


为什么这么说呢,因为使用上面两个API并不能减小程序的内存,只不过是强制将正在运行的程序工作内存写入Windows的页面文件。这样看似内存使用量是下降了,其实只不过是把内存转到了慢速存储之中。当程序再次需要使用到这些内存时,由于内存已经移入页面文件,虚拟内存位置被标记为了"不存在",这时将会产生一个缺页中断,也就是平常说的页面错误,这时,操作系统又不得不重新将这些内存由硬盘移到内存之中。


比如你正在看一个视频,现在先暂停它,接着使用内存清理工具来“清理”内存。清理完后,你再回过头来继续播放刚才的视频或者执行其他程序,你会发现,程序略有卡顿(对于小内存机器尢为明显),在卡顿过后,系统的内存又有所上升,一段时间后,内存使用量也慢慢又涨上去了。如果你在刚清理后使用某个程序并在系统任务管理器里查看进程信息(需要在“选择列”对话框中选中“页面错误增量”),你会发现该程序的页面错误增量会明显地增加,这个时候系统就正在把内存重新搬回到工作集内存之中。



曾有一文就这一问题进行了深入分析,在此一并贴出:


Why Memory Optimizers and RAM Boosters Are Worse Than Useless


Many companies want to sell you “memoryoptimizers,” often as part of “PC optimization” programs. These programs areworse than useless — not only will they not speed up your computer, they’llslow it down.

Such programs take advantage ofinexperienced users, making false promises about boosting performance. Inreality, your computer knows how to manage RAM on its own. It will use RAM toincrease your computer’s performance — there’s no point in having RAM sitempty.

Is Your Computer’s RAM Filling Up? That’s Good!

Memory optimizers are based on amisunderstanding. You may look at your computer’s RAM and see it filling up —for example, you may have 4 GB of RAM and see that 3 GB is full with only 1 GBto spare. That can be surprising to some people — look how bloated modernversions of Windows are! How are you ever going to run additional programs withso little memory available?

In reality, modern operating systems arepretty good at managing memory on their own. That 3 GB of used RAM doesn’tnecessarily indicate waste. Instead, your computer uses your RAM to cache datafor faster access. Whether it’s copies of web pages you had open in yourbrowser, applications you previously opened, or any other type of data youmight need again soon, your computer hangs onto it in its RAM. When you needthe data again, your computer doesn’t have to hit your hard drive — it can justload the files from RAM.

Crucially, there’s no point inhaving RAM empty. Even if your RAM is completely full and your computer needsmore of it to run an application, your computer can instantly discard thecached data from your RAM and use that space for the application. There’s nopoint in having RAM sit empty — if it’s empty, it’s being wasted. If it’s full,there’s a good chance it can help speed up program loading times and anythingelse that would use your computer’s hard drive.

Notice that very little RAM is actually“free” in the screenshot below. The RAM is being used as a cache, but it’sstill marked as available for any program that needs to use it.


In the past, full RAM did indicate aproblem. If you were running Windows Vista on a computer with half a gig ofRAM, you could feel the computer constantly slowing down — it had to constantlyread and write to the hard drive, using the hard drive’s page file as aninefficient replacement for RAM. However, modern computers generally haveenough RAM for most users. Even low-end computers generally ship with 4GB of RAM, which should be more than enough unless you’re doing intensive gaming,running multiple virtual machines, or editing videos.

Even if RAM was a problem for you,there’s no reason to use a memory optimizer. Memory optimizers are snake oilthat are useless at best and harmful at worst.

How Memory Optimizers Work

When you use a memory optimizer, you’llsee your computer’s RAM usage go down. This may seem like an easy win — you’vedecreased RAM usage just be pressing a button, after all. But it’s not thatsimple.

Memory optimizers actually work in oneof two ways:

·        They call the EmptyWorkingSet Windows API function,forcing running applications to write their working memory to the Windows pagefile.

·        They quickly allocate a large amount of memory tothemselves, forcing Windows to discard cached data and write application datato the page file. They then deallocate the memory, leaving it empty.

Both of these tricks will indeed free upRAM, making it empty. However, all this does is slow things down — now theapplications you use will have to get the data they need from the page file,reading from the hard drive and taking longer to work. Any memory being usedfor cache may be discarded, so Windows will have to get the data it needs fromthe hard drive.

In other words, these programs free upfast memory by forcing data you need onto slower memory, where it will have tobe moved back to fast memory again. This makes no sense! All it accomplishes isselling you another system optimization program you don’t need.


If Windows needs RAM, it will push datato the page file or discard cached data, anyway. This all happens automaticallywhen it needs to — there’s no point in slowing things down by forcing it to happenbefore it's necessary.

Like PC cleaning apps, memoryoptimizers are a scam. They appear to be doing something positive to people whodon’t understand how memory management works, yubut they’re actually doingsomething harmful.

How to Actually “Optimize” Your Memory

If you do want to have more availableRAM, skip the memory optimizer. Instead, try to get rid of running applicationsyou don’t need — purge unnecessary programs from your system tray, disableuseless startup programs, and so on.

If you do need more RAM for what you do,try buying some more RAM. RAM is pretty cheap and it’s not too hard to installit yourself using one of the RAM installing guides available online.Just ensure you buy the correct type of RAM for your computer.



文中从内存管理本质上来说明这类工具不仅无所贡献,反而可谓“越帮越忙”。对于现在的操作系统,并不是你内存空闲越多,程序运行就越快。就算你系统内存真快耗尽,操作系统也会自动丢弃掉一部分缓存数据,同时将不频繁访问的页面从工作集中移出,暂时保存在内存中的“转换列表”中,或者进一步换出到页面文件中,进而腾出内存供需要的程序使用,我们自己完全没有理由也没有必要在不必要的时候做这些“无厘头”的事。


至此,想必大家已经从本质上明白了这类工具实现的原理。对于上面实现的程序只是一个简单地展示,不建议(甚至可以说不应该)用于解决内存不足的问题。


基本网上各种内存清理工具使用的都是这种方式,更有甚者,有的工具还允许设置自动清理,可以每隔一定时间自动进行清理...... 当你明白了这些工具的实现原理后,你会发现,这类工具仅仅是一个骗局而已,它们只不过是在安慰那些不懂内存管理的人罢了。


除了使用这种方式,有些清理工具还进行暴力清理。这类工具自身大量申请内存,快速填满你的内存,这时你的系统会被迫丢弃大量缓存文件,同时调用转换操作,将其他进程的内存空间转换到虚拟内存。之后,这类工具再突然释放所申请的大量空间,让你觉得腾出了很大的空间。使用这种暴力清理可能腾出比使用EmptyWorkingSet或SetProcessWorkingSetSize更多的内存空间,但在所谓的“清理”过程中的开销也更大,最终同样也只是让事情变得更加糟糕。


也有部分清理内存工具,还会结束掉一些闲置的服务与一些进程的残留项以进一步减小内存使用量(如360安全卫士)。这种方式确实有一定的效果,能真正地腾出一定的空间。但由于可以结束的进程是有限的,而且这些进程所占内存往往不会太大,通常也不能够腾出多少内存空间,优化效果并不会明显。


综上,我个人认为,无论清理内存或者内存优化之类的工具没什么实用价值与实际意义。对于内存小的机器,它们只会更加拖慢你的系统;对于大内存的机器,就算它们有用,你也没有理由去用。你清或者不清,它就在那里,不增不减。所以,要解决内存不够的问题,最有效的方式就是——插内存条插内存条插内存条


8
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:15799次
    • 积分:276
    • 等级:
    • 排名:千里之外
    • 原创:5篇
    • 转载:3篇
    • 译文:0篇
    • 评论:22条
    文章分类
    最新评论