Windows Server 2008 R2 图元文件 RAM 使用情况

al2*_*2k4 33 memory windows-server-2008-r2

我有一台运行 Windows Server 2008 R2 x64 的服务器,具有 4GB 的 RAM,它承载大约 2-3 百万个文件,其中大部分是图像文件。

在一周的时间里,我注意到服务器上的应用程序由于内存不足导致过度分页到磁盘而缓慢爬行,这对当前在其上运行的所有服务产生连锁反应,导致主要性能问题。

在任务管理器中进行调查时,我注意到几乎所有 4GB 都在使用中,但是当您查看“进程”选项卡时,所有内存使用量的总和并没有加起来,最多只有 1.5GB 应该在使用中。

使用谷歌找到解决方案,似乎大部分 RAM 都用于“元文件”,它是文件系统上文件的 NTFS 信息的缓存,这样系统就不必再次向 MFT 查询信息。这个缓存永远不会被清除或在任务管理器中被标记为“缓存”或在 Sysinternal 的 RamMap 中被标记为“备用”。

有人建议安装 KB979149 修补程序,但在尝试安装时,提示“此更新不适用于您的计算机”。

到目前为止,我发现的唯一临时修复是:

  1. 每 1-3 天使用来自 Sysinternals 的 RAMmap 到“清空系统工作集”,这将缓存在任务管理器中标记为“备用”和“缓存”,以便其他应用程序可以使用 RAM。
  2. 重新启动机器,这是不可取的,因为该服务器正在为公共网站提供服务。

目前,我必须每隔几天执行一次 2. 修复,以防止它达到瓶颈水平。

之前:(使用了 800 MB RAM - 其他应用程序无法使用此 RAM)

在此处输入图片说明

之后:(800 MB RAM 标记为缓存 - 可用于其他应用程序)

所以我对你们所有人的问题是:是否存在任何方法来限制此元文件的 RAM 使用量?

Beo*_*e42 16

处理此问题的最佳方法是使用SetSystemFileCacheSizeAPI 作为MS KB976618 指示 用于指示.

不要定期清除缓存

使用该SetSystemFileCacheSize功能而不是定期清除缓存可以提高性能和稳定性。定期清除缓存会导致从内存中清除过多的元文件和其他信息,Windows 将不得不将所需的信息从 HDD 重新读回 RAM。每当您清除缓存时,这会导致性能突然严重下降几秒钟,随后良好的性能会随着内存充满元文件数据而缓慢下降。

使用该SetSystemFileCacheSize函数设置最小值和最大值,这将导致 Windows 将多余的旧图元文件数据标记为备用内存,正常缓存功能可以根据当前资源需求和正常缓存优先级使用或丢弃这些内存。这也允许比您设置的活动内存最大值更多的元文件数据作为备用数据在内存中,如果 Windows 没有将内存用于其他任何事情,同时保持足够的可用内存。这是始终保持系统性能特性良好的理想情况。

MS 不支持第三方程序

如果你像我一样不想在你的生产服务器上运行来自某个未知第三方的二进制文件,你需要一个官方的 MS 工具或一些可以在这些服务器上运行之前检查的代码。2008 R2 的 DynCache 工具几乎不可能在不支付支持案例的情况下从 M$ 获得,而且坦率地说,基于 2008 年的代码,它似乎过于臃肿,因为 Windows 已经具有动态调整大小所需的内置逻辑缓存——它只需要知道适合您系统的最大值。

解决以上所有问题

我写了一个适用于 64 位机器的 Powershell 脚本。您需要以具有提升权限的管理员身份运行它。您应该能够在任何 x64 windows Vista / Server 2008 上运行它,直到并包括 10 / Server 2012 R2 和任意数量的 RAM。您不需要安装任何额外的软件,因此可以让您的服务器/工作站得到 MS 的完全支持。

您应该在每次启动时使用提升的权限运行此脚本,以使设置永久有效。Windows 任务计划程序可以为您执行此操作。如果 Windows 安装在虚拟机内,并且您更改了分配给该 VM 的 RAM 量,则还应在更改后运行它。

您可以随时在正在运行的系统上运行此脚本,即使是在生产使用中,也无需重新启动系统或关闭任何服务。

# 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
Run Code Online (Sandbox Code Playgroud)

顶部附近有一条线表示$MaxPercent = 12.5将新的最大工作集(活动内存)设置为总物理 RAM 的 12.5%。Windows 将根据系统需求动态调整活动内存中元文件数据量的大小,因此您无需动态调整此最大值。

这不会解决您在映射文件缓存变得太大时遇到的任何问题。

我还制作了一个GetSystemFileCacheSizePowershell 脚本并将其发布在 StackOverflow 上


编辑:我还应该指出,您不应多次从同一个 Powershell 实例运行这 2 个脚本中的任何一个,否则您将收到Add-Type已进行调用的错误。

编辑:将SetSystemFileCacheSize脚本更新到 1.1 版,为您计算适当的最大缓存值并具有更好的状态输出布局。

编辑:现在我已经升级了我的 Windows 7 笔记本电脑,我可以告诉你该脚本在 Windows 10 中成功运行,但我还没有测试它是否仍然需要。但是即使移动虚拟机硬盘文件,我的系统仍然稳定。