วิธีที่ดีที่สุดในการจัดการกับปัญหานี้คือการใช้SetSystemFileCacheSize
API เป็นMS KB976618 สั่ง ที่ใช้ในการออกคำสั่ง
อย่าล้างแคชเป็นระยะ
การใช้SetSystemFileCacheSize
ฟังก์ชันแทนที่จะล้างแคชจะปรับปรุงประสิทธิภาพและเสถียรภาพเป็นระยะ การล้างแคชเป็นระยะจะส่งผลให้ metafile มากเกินไปและข้อมูลอื่น ๆ ถูกลบออกจากหน่วยความจำและ Windows จะต้องอ่านข้อมูลที่จำเป็นกลับไปสู่ RAM จาก HDD สิ่งนี้จะทำให้ประสิทธิภาพการทำงานลดลงอย่างฉับพลันและรุนแรงเป็นเวลาหลายวินาทีเมื่อใดก็ตามที่คุณล้างแคชตามด้วยประสิทธิภาพที่ดีซึ่งจะค่อยๆลดลงเมื่อหน่วยความจำเต็มไปด้วยข้อมูล metafile
การใช้SetSystemFileCacheSize
ฟังก์ชั่นตั้งค่าต่ำสุดและสูงสุดที่จะทำให้ Windows ตั้งค่าสถานะข้อมูลเมตาไฟล์เก่าเกินเป็นหน่วยความจำสแตนด์บายที่ฟังก์ชั่นแคชปกติสามารถใช้หรือทิ้งตามความต้องการทรัพยากรในปัจจุบันและลำดับความสำคัญแคชปกติ สิ่งนี้ยังช่วยให้ข้อมูล metafile มากกว่าหน่วยความจำสูงสุดที่คุณใช้งานอยู่ในหน่วยความจำในฐานะข้อมูลสแตนด์บายหาก Windows ไม่ได้ใช้หน่วยความจำอย่างอื่นในขณะที่ยังคงรักษาหน่วยความจำที่มีอยู่มากมาย นี่เป็นสถานการณ์ในอุดมคติที่ทำให้คุณสมบัติของระบบดีตลอดเวลา
โปรแกรมของบุคคลที่สามไม่รองรับโดย MS
หากคุณเป็นเหมือนฉันและไม่ต้องการเรียกใช้ไบนารีจากบุคคลที่สามที่ไม่รู้จักในเซิร์ฟเวอร์การผลิตของคุณคุณต้องการเครื่องมือ MS อย่างเป็นทางการหรือรหัสบางอย่างที่คุณสามารถตรวจสอบก่อนที่จะทำงานบนเซิร์ฟเวอร์เหล่านั้น เครื่องมือ DynCache สำหรับ 2008 R2 เป็นไปไม่ได้ในทางปฏิบัติที่จะได้รับจาก M $ โดยไม่ต้องจ่ายเงินสำหรับกรณีการสนับสนุนและค่อนข้างตรงไปตรงมาตามรหัสสำหรับปี 2008 ดูเหมือนว่าป่องๆมากเกินไปสำหรับงานเนื่องจาก Windows มีตรรกะในตัว แคช - เพียงแค่ต้องการทราบค่าสูงสุดที่เหมาะสมสำหรับระบบของคุณ
วิธีแก้ปัญหาทั้งหมดที่กล่าวมา
ฉันเขียนสคริปต์ Powershell ที่ทำงานบนเครื่อง 64 บิต คุณต้องเรียกใช้ในฐานะผู้ดูแลระบบด้วยสิทธิ์ระดับสูง คุณควรจะสามารถรันมันได้ตามที่เป็นอยู่ใน x64 windows Vista / Server 2008 ใด ๆ จนถึงและรวมถึง 10 / Server 2012 R2 ด้วย RAM จำนวนเท่าใดก็ได้ คุณไม่จำเป็นต้องติดตั้งซอฟต์แวร์เพิ่มเติมใด ๆ และทำให้เซิร์ฟเวอร์ / เวิร์กสเตชันของคุณรองรับอย่างเต็มที่โดย MS
คุณควรรันสคริปต์นี้ทุกครั้งที่มีการยกระดับสิทธิ์เพื่อให้การตั้งค่าเป็นแบบถาวร Windows Task Scheduler สามารถทำสิ่งนี้ให้คุณได้ หากการติดตั้ง Windows อยู่ในเครื่องเสมือนและคุณเปลี่ยนจำนวน RAM ที่จัดสรรให้กับ VM นั้นคุณควรรันหลังจากการเปลี่ยนแปลง
คุณสามารถเรียกใช้สคริปต์นี้ได้ตลอดเวลาบนระบบที่ใช้งานแม้ในขณะที่ใช้งานจริงโดยไม่ต้องรีบูตระบบหรือปิดบริการใด ๆ
# Filename: setfc.ps1
$version = 1.1
#########################
# Settings
#########################
# The percentage of physical ram that will be used for SetSystemFileCache Maximum
$MaxPercent = 12.5
#########################
# Init multipliers
#########################
$OSBits = ([System.IntPtr]::Size) * 8
switch ( $OSBits)
{
32 { $KiB = [int]1024 }
64 { $KiB = [long]1024 }
default {
# not 32 or 64 bit OS. what are you doing??
$KiB = 1024 # and hope it works anyway
write-output "You have a weird OS which is $OSBits bit. Having a go anyway."
}
}
# These values "inherit" the data type from $KiB
$MiB = 1024 * $KiB
$GiB = 1024 * $MiB
$TiB = 1024 * $GiB
$PiB = 1024 * $TiB
$EiB = 1024 * $PiB
#########################
# Calculated Settings
#########################
# Note that because we are using signed integers instead of unsigned
# these values are "limited" to 2 GiB or 8 EiB for 32/64 bit OSes respectively
$PhysicalRam = 0
$PhysicalRam = [long](invoke-expression (((get-wmiobject -class "win32_physicalmemory").Capacity) -join '+'))
if ( -not $? ) {
write-output "Trying another method of detecting amount of installed RAM."
}
if ($PhysicalRam -eq 0) {
$PhysicalRam = [long]((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory) # gives value a bit less than actual
}
if ($PhysicalRam -eq 0) {
write-error "Cannot Detect Physical Ram Installed. Assuming 4 GiB."
$PhysicalRam = 4 * $GiB
}
$NewMax = [long]($PhysicalRam * 0.01 * $MaxPercent)
# The default value
# $NewMax = 1 * $TiB
#########################
# constants
#########################
# Flags bits
$FILE_CACHE_MAX_HARD_ENABLE = 1
$FILE_CACHE_MAX_HARD_DISABLE = 2
$FILE_CACHE_MIN_HARD_ENABLE = 4
$FILE_CACHE_MIN_HARD_DISABLE = 8
################################
# C# code
# for interface to kernel32.dll
################################
$source = @"
using System;
using System.Runtime.InteropServices;
namespace MyTools
{
public static class cache
{
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool GetSystemFileCacheSize(
ref IntPtr lpMinimumFileCacheSize,
ref IntPtr lpMaximumFileCacheSize,
ref IntPtr lpFlags
);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetSystemFileCacheSize(
IntPtr MinimumFileCacheSize,
IntPtr MaximumFileCacheSize,
Int32 Flags
);
[DllImport("kernel32", CharSet = CharSet.Unicode)]
public static extern int GetLastError();
public static bool Get( ref IntPtr a, ref IntPtr c, ref IntPtr d )
{
IntPtr lpMinimumFileCacheSize = IntPtr.Zero;
IntPtr lpMaximumFileCacheSize = IntPtr.Zero;
IntPtr lpFlags = IntPtr.Zero;
bool b = GetSystemFileCacheSize(ref lpMinimumFileCacheSize, ref lpMaximumFileCacheSize, ref lpFlags);
a = lpMinimumFileCacheSize;
c = lpMaximumFileCacheSize;
d = lpFlags;
return b;
}
public static bool Set( IntPtr MinimumFileCacheSize, IntPtr MaximumFileCacheSize, Int32 Flags )
{
bool b = SetSystemFileCacheSize( MinimumFileCacheSize, MaximumFileCacheSize, Flags );
if ( !b ) {
Console.Write("SetSystemFileCacheSize returned Error with GetLastError = ");
Console.WriteLine( GetLastError() );
}
return b;
}
}
public class AdjPriv
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = new IntPtr(processHandle);
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
if(disable)
{
tp.Attr = SE_PRIVILEGE_DISABLED;
} else {
tp.Attr = SE_PRIVILEGE_ENABLED;
}
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
}
}
"@
# Add the c# code to the powershell type definitions
Add-Type -TypeDefinition $source -Language CSharp
#########################
# Powershell Functions
#########################
function output-flags ($flags)
{
Write-output ("FILE_CACHE_MAX_HARD_ENABLE : " + (($flags -band $FILE_CACHE_MAX_HARD_ENABLE) -gt 0) )
Write-output ("FILE_CACHE_MAX_HARD_DISABLE : " + (($flags -band $FILE_CACHE_MAX_HARD_DISABLE) -gt 0) )
Write-output ("FILE_CACHE_MIN_HARD_ENABLE : " + (($flags -band $FILE_CACHE_MIN_HARD_ENABLE) -gt 0) )
Write-output ("FILE_CACHE_MIN_HARD_DISABLE : " + (($flags -band $FILE_CACHE_MIN_HARD_DISABLE) -gt 0) )
write-output ""
}
#########################
# Main program
#########################
write-output ""
#########################
# Get and set privilege info
$ProcessId = $pid
$processHandle = (Get-Process -id $ProcessId).Handle
$Privilege = "SeIncreaseQuotaPrivilege"
$Disable = $false
Write-output ("Enabling SE_INCREASE_QUOTA_NAME status: " + [MyTools.AdjPriv]::EnablePrivilege($processHandle, $Privilege, $Disable) )
write-output ("Program has elevated privledges: " + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") )
write-output ""
whoami /PRIV | findstr /I "SeIncreaseQuotaPrivilege" | findstr /I "Enabled"
if ( -not $? ) {
write-error "user Security Token SE_INCREASE_QUOTA_NAME: Disabled`r`n"
}
write-output "`r`n"
#########################
# Get Current Settings
# Init variables
$SFCMin = 0
$SFCMax = 0
$SFCFlags = 0
#Get Current values from kernel
$status = [MyTools.cache]::Get( [ref]$SFCMin, [ref]$SFCMax, [ref]$SFCFlags )
#typecast values so we can do some math with them
$SFCMin = [long]$SFCMin
$SFCMax = [long]$SFCMax
$SFCFlags = [long]$SFCFlags
write-output "Return values from GetSystemFileCacheSize are: "
write-output "Function Result : $status"
write-output " Min : $SFCMin"
write-output (" Max : $SFCMax ( " + $SFCMax / 1024 / 1024 / 1024 + " GiB )")
write-output " Flags : $SFCFlags"
output-flags $SFCFlags
#########################
# Output our intentions
write-output ("Physical Memory Detected : $PhysicalRam ( " + $PhysicalRam / $GiB + " GiB )")
write-output ("Setting Max to " + $MaxPercent + "% : $NewMax ( " + $NewMax / $MiB + " MiB )`r`n")
#########################
# Set new settings
$SFCFlags = $SFCFlags -bor $FILE_CACHE_MAX_HARD_ENABLE # set max enabled
$SFCFlags = $SFCFlags -band (-bnot $FILE_CACHE_MAX_HARD_DISABLE) # unset max dissabled if set
# or if you want to override this calculated value
# $SFCFlags = 0
$status = [MyTools.cache]::Set( $SFCMin, $NewMax, $SFCFlags ) # calls the c# routine that makes the kernel API call
write-output "Set function returned: $status`r`n"
# if it was successfull the new SystemFileCache maximum will be NewMax
if ( $status ) {
$SFCMax = $NewMax
}
#########################
# After setting the new values, get them back from the system to confirm
# Re-Init variables
$SFCMin = 0
$SFCMax = 0
$SFCFlags = 0
#Get Current values from kernel
$status = [MyTools.cache]::Get( [ref]$SFCMin, [ref]$SFCMax, [ref]$SFCFlags )
#typecast values so we can do some math with them
$SFCMin = [long]$SFCMin
$SFCMax = [long]$SFCMax
$SFCFlags = [long]$SFCFlags
write-output "Return values from GetSystemFileCacheSize are: "
write-output "Function Result : $status"
write-output " Min : $SFCMin"
write-output (" Max : $SFCMax ( " + $SFCMax / 1024 / 1024 / 1024 + " GiB )")
write-output " Flags : $SFCFlags"
output-flags $SFCFlags
มีบรรทัดใกล้ด้านบนที่ระบุ$MaxPercent = 12.5
ว่าตั้งค่าชุดการทำงานสูงสุดใหม่ (หน่วยความจำที่ใช้งาน) เป็น 12.5% ของ RAM ทั้งหมดที่มีอยู่จริง Windows จะปรับขนาดข้อมูล metafile ในหน่วยความจำที่ใช้งานอยู่ตามความต้องการของระบบแบบไดนามิกดังนั้นคุณไม่จำเป็นต้องปรับค่าสูงสุดนี้แบบไดนามิก
นี่จะไม่แก้ไขปัญหาใด ๆ ที่คุณมีกับแคชไฟล์ที่แมปมีขนาดใหญ่เกินไป
ผมได้ทำยังมีGetSystemFileCacheSize
สคริปต์ Powershell และโพสต์ไว้ใน StackOverflow
แก้ไข: ฉันควรชี้ให้เห็นว่าคุณไม่ควรเรียกใช้สคริปต์ 2 ตัวใดตัวหนึ่งจากอินสแตนซ์ Powershell เดียวกันมากกว่าหนึ่งครั้งหรือคุณจะได้รับข้อผิดพลาดที่มีการAdd-Type
โทรแล้ว
แก้ไข: SetSystemFileCacheSize
สคริปต์ที่อัปเดตเป็นรุ่น 1.1 ที่คำนวณค่าแคชสูงสุดที่เหมาะสมสำหรับคุณและมีโครงร่างเอาต์พุตสถานะ nicer
แก้ไข: ตอนนี้ฉันได้อัพเกรดแล็ปท็อป Windows 7 ของฉันฉันสามารถบอกคุณได้ว่าสคริปต์ทำงานได้สำเร็จใน Windows 10 แม้ว่าฉันจะยังไม่ได้ทดสอบว่ายังจำเป็นต้องใช้หรือไม่ แต่ระบบของฉันยังคงมีเสถียรภาพแม้ในขณะที่ย้ายไฟล์ HDD เครื่องเสมือนไปรอบ ๆ