Windows Server 2008 R2 x64を実行するサーバーで、4 GBのRAMが約200万〜300万のファイルをホストし、その大部分が画像ファイルです。
1週間にわたって、メモリ不足によるディスクへの過剰なページングが原因で、サーバー上のアプリケーションがクロールに遅くなり、現在実行されているすべてのサービスにノックオン効果があり、重大な問題が発生していることに気付きましたパフォーマンスの問題。
タスクマネージャーで調査したところ、4 GBのほぼすべてが使用中であることに気付きましたが、[プロセス]タブを見ると、そこにあるすべてのメモリ使用量の合計は合計されず、使用されているのは最大で1.5 GBにすぎません。
Googleを使用して解決策を見つけると、RAMのほとんどがファイルシステム上のファイルのNTFS情報のキャッシュである「メタファイル」で使用されたため、システムにこのキャッシュは、タスクマネージャーではクリアされず、「キャッシュ」としてマークされたり、SysinternalのRamMapでは「スタンバイ」としてマークされたりすることはありません。
KB979149ホットフィックスをインストールするための 提案 がありましたが、インストールしようとすると、「このアップデートはお使いのコンピューターには適用されません」と表示されます。
これまでに見つけた唯一の一時的な修正は次のとおりです。
現時点では、ボトルネックレベルに達しないように、数日ごとに2.修正を実行する必要があります。
以前:(800 MB RAM使用-他のアプリケーションはこのRAMを使用できません)
後:(800 MB RAMキャッシュとしてマーク-他のアプリケーションで利用可能)
ですから、皆さんへの私の質問は、このメタファイルのRAM使用を制限するための方法はありますか?
この問題に対処する最善の方法は、 SetSystemFileCacheSize
APIを MS KB976618 として使用することです。 指示する 指示に使用されました 。
定期的にキャッシュをクリアしないでください
キャッシュを定期的にクリアするのではなくSetSystemFileCacheSize
関数を使用すると、パフォーマンスと安定性が向上します。キャッシュを定期的にクリアすると、メタファイルやその他の情報がメモリから消去され、Windowsは必要な情報をHDDからRAM)に再度読み込む必要があります。これにより、突然の深刻な問題が発生します。キャッシュをクリアするたびに数秒間パフォーマンスが低下し、その後、メモリがメタファイルデータで一杯になると、パフォーマンスが徐々に低下します。
SetSystemFileCacheSize
関数を使用すると、Windowsが過剰な古いメタファイルデータをスタンバイメモリとしてフラグ付けする最小値と最大値が設定され、通常のキャッシュ機能が現在のリソース要求と通常のキャッシュ優先順位に従って使用または破棄できます。これにより、設定したアクティブメモリの最大値よりも多くのメタファイルデータを使用でき、Windowsが他の目的でメモリを使用していない場合は、使用可能なメモリを十分に維持しながら、スタンバイデータとしてメモリに格納できます。これは、システムのパフォーマンス特性を常に良好に保つ理想的な状況です。
MSはサードパーティプログラムをサポートしていません
あなたが私のようなもので、未知のサードパーティのバイナリを運用サーバーで実行したくない場合は、それらのサーバーで実行する前に検査できる公式のMSツールまたはコードが必要です。 2008 R2のDynCacheツールは、サポートケースを支払うことなくM $から取得することは事実上不可能であり、率直に言って、2008年のコードに基づいて、動的にサイズ変更するために必要な組み込みロジックがWindowsに既にあるため、タスクに対して過度に肥大化しているようですキャッシュ—それはあなたのシステムに適切な最大値を知る必要があるだけです。
上記すべての解決策
64ビットマシンで動作するPowershellスクリプトを作成しました。管理者特権で管理者として実行する必要があります。それは、任意の量のRAMを搭載したx64 Windows Vista/Server 2008から10/Server 2012 R2までをそのまま実行できるはずです。追加のソフトウェアをインストールする必要はなく、その結果、サーバー/ワークステーションは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
上部に$MaxPercent = 12.5
と書かれた行があり、新しい最大ワーキングセット(アクティブメモリ)が物理RAM全体の12.5%に設定されています。 Windowsは、システムの要求に基づいてアクティブなメモリ内のメタファイルデータの量を動的にサイズ調整するため、この最大値を動的に調整する必要はありません。
これにより、マップされたファイルキャッシュが大きくなりすぎる問題は修正されません。
GetSystemFileCacheSize
Powershellスクリプトと StackOverflowに投稿 も作成しました。
編集:また、同じPowershellインスタンスからこれら2つのスクリプトのいずれかを2回以上実行しないでください。そうしないと、Add-Type
呼び出しが既に行われているというエラーが表示されます。
編集:SetSystemFileCacheSize
スクリプトをバージョン1.1に更新し、適切な最大キャッシュ値を計算し、ステータス出力レイアウトを改善しました。
編集:Windows 7ラップトップをアップグレードしたので、スクリプトがWindows 10で正常に実行されることを確認できますが、まだ必要かどうかはテストしていません。しかし、仮想マシンのHDDファイルを移動しても、システムはまだ安定しています。
私は、Windows OSのメモリまたはディスクキャッシングの内部動作に関する専門家であるとは主張していませんが、2つの見解があります。
OSがメモリにデータをキャッシュしなかった場合、ディスクから読み取る必要があります。これはメモリよりも指数関数的に遅いストレージメディアであるため、今見ているパフォーマンスの問題はほぼ確実に悪化します。
問題の原因ではなく、問題の症状を扱うことによって問題を解決しようとしています。問題の原因は、ほぼ確実に十分な物理的なRAMの欠如であり、私の提案はそれに対処することです。
さらに、キャッシュは1.5GBのRAM=を使用している可能性がありますが、他のプロセスやサービスのメモリ使用量はどうなるのでしょうか。解決策は、その使用量を潜在的な問題について調査することです。
RAMを追加するだけの明らかであるが効果のない解決策を提供した人々にとって、この問題に直接対処していないことは明らかです。
以前のポスターで述べたように、RAM問題にどれだけの量を投げるかは問題ではありません...それはすべて満たされます。私は、アプリケーションサーバーでAtlassianツールセットを実行しています。は32ビット(2003)から64ビット(2008)に移行されましたが、パフォーマンスが低下していることがすぐにわかりました。
タスクマネージャを見ると、ほぼすべてのメモリが消費されていました。実行中のプロセスはこれを反映していませんが。メモリを8 GBから16 GBに増やしたとき、問題は追加のメモリも消費しました。
この問題を処理する唯一の方法は、サーバーを再起動することでした。これにより、プロセスと同じメモリ使用量(約3.5 GB)が低下しました。これは1日かそこらの内に再び登り始めました。
これが新しいMicrosoftのバグ/機能であることを知っていて、この記事を見つけてうれしかったです。ユーザーが理解できるように、Microsoftがこの非常に重要な詳細を残しているところが気に入っています。 RamMapをダウンロードしました。これはネイティブユーティリティと思われますが、メタファイルの使用状況を確認できます。キャッシュは数日ごとにクリアされるように設定し、うまくいけばこれで問題が解決します。
興味深いのは、移行したサーバーのいくつかのうちの1つでのみこの問題を見たことがあるので、メタファイルが特定の種類のアプリケーションからのみ供給されるのかどうか疑問に思っています。
この問題は、SysInternals CacheSetツールを使用して迅速かつ無料で対処できます。ワーキングセットの最大値をシステムRAMの量よりも小さい適切な値に設定して適用するだけです。
これは、Microsoft DynCacheツールをダウンロードするためのリンクです。チケットを作成したり、支払いを行ったりする必要はありません。 http://www.Microsoft.com/en-us/download/details.aspx?displaylang=en&id=9258
(申し訳ありません-これはR2バージョンではないことに注意してください)
継続的なキャッシュ増加の既知の問題については、Microsoftブログで説明しています: http://blogs.msdn.com/b/ntdebugging/archive/2007/11/ 27/too-much-cache.aspx
[更新] Windows Server 2008 R2の動作中の修正。
CodeplexでサンプルC#コードを見つけ、すぐにVisual StudioでコンソールC#プロジェクトを作成し、コンパイルして動作しました。
https://asstoredprocedures.svn.codeplex.com/svn/ASSP/FileSystemCache.cs
注:ここにあるMicrosoft.AnalysisServices.AdomdClientへの参照を追加する必要があります。
C:\ Program Files(x86)\ Microsoft.NET\ADOMD.NET
そして、(私の場合)XMLaDiscoverへの不要な参照でClearAllCaches()メソッドをコメント化します。これをTaskSchedulerにスローします。
直接になって申し訳ありませんが、最近のワークステーションのワークロードよりも少し高いRAMにサーバーをアップグレードする場合はどうでしょうか。 16GB Memroyは安っぽくなっています。あなたの時間の半日よりも安価です。
メタファイルによるRAMの使用を制限できるようにするMSからDynCacheツールを入手できます。