tech.guitarrapc.cóm

Technical updates

PowerShell でメモリ制限を調整する

PoweShellを利用していると、System.OutOfMemoryException の表示にめぐり会う時がきます。 トテモザンネンですが出会うのです。 特に Get-ChildItem -Recurse | Select-String hogehoge などという処理をすると巡り会いやすくなります。 今回は、PowerShell のShellに割り当てられたメモリの調整についてです。

WSMAN: ドライブの調整にあたって前提

Windows PowerShell の諸々調整は、WSMAN: ドライブで行えます。 以下を調整にあたり注意してください。
  1. WSMANのパラメータ変更操作には管理者権限が必要なのでご注意ください。
  2. WinRMサービスが動いていることが必要です (Get-Service -Name WinRM)

マシン全体のチューニング

まずはマシン全体の制限を見ましょう。 WSManドライブのlocalhost\Shellを確認します。
cd WSMan:
ls localhost\Shell
調整すべきは、MaxMemoryPerShellMBパラメータです。 (以下のコードはWSManドライブに移動せずとも確認できます) デフォルトでは、1024MBであることが分かります。
Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB
パラメータを調整するには、Set-Item コマンドレットを利用します。 例えば 2048MB に調整するには、次のようにします。
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 2048
調整をかけると次の警告が出ます。
WARNING: The updated configuration might affect the operation of the plugins having a per plugin quota value greater than 2048. Verify the configuration of all the registered plugins and change the per plugin quota values for the affected plugins.

プラグインのチューニング

先の警告の通り、PowerShell 3.0においては、 プラグインのメモリ調整も必要です。 が、プラグイン色々あるのです。
ls WSMan:localhost\Plugin
調整すべきは、この中の microsoft.powershell です。
   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin

Type            Keys                                Name
----            ----                                ----
Container       {Name=Event Forwarding Plugin}      Event Forwarding Plugin
Container       {Name=microsoft.powershell}         microsoft.powershell
Container       {Name=microsoft.powershell.workf... microsoft.powershell.workflow
Container       {Name=microsoft.powershell32}       microsoft.powershell32
Container       {Name=WMI Provider}                 WMI Provider
以下のコマンドで現在のQuotaを知ることができます。
Get-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell
デフォルトのままなら恐らく1000MBと表示されると思います。
   WSManConfig: Microsoft.WSMan.Management\WSMan::localhost\Plugin\microsoft.powershell\Quotas

Type            Name                           SourceOfValue   Value
----            ----                           -------------   -----
System.String   MaxConcurrentCommandsPerShell                  1000
これもマシン設定と同様に調整します。 今回の場合は、2048MBです。
Set-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell 2048
調整をかけると次の警告が出ます。
WARNING: The configuration changes you made will only be effective after the WinRM service is restarted. To restart the WinRM service, run the following command: 'Restart-Service winrm'

WinRMの再起動による変更の適用

変更を適用するには、WinRMを再起動します。
Restart-Service WinRM
最後に現在値が変更されていることを確認します。
# Machine Configuration
Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB

# Endpoint Plugin Configuration
Get-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell

ファンクション化

面倒なのでfunctionにしてしまいます。
function Set-PowerShellMemoryTuning{

    param(
        [parameter(
            position = 0,
            mandatory = 1)]
        [ValidateNotNullorEmpty()]
        [ValidateRange(1,2147483647)]
        [int]
        $memory
    )

    # Test Elevated or not
    $TestElevated = {
        $user = [Security.Principal.WindowsIdentity]::GetCurrent()
        (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
    }

    if (&$TestElevated)
    {

        # Machine Wide Memory Tuning
        Write-Warning "Current Memory for Machine wide is : $((Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB).value) MB"

        Write-Warning "Change Memory for Machine wide to : $memory MB"
        Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB $memory


        # EndPoing Memory Tuning
        Write-Warning "Current Memory for Plugin is : $((Get-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell).value) MB"

        Write-Warning "Change Memory for Plugin to : $memory MB"
        Set-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell $memory


        # Restart WinRM
        Write-Warning "Restarting WinRM"
        Restart-Service WinRM -Force -PassThru


        # Show Current parameters
        Write-Warning "Current Memory for Machine wide is : $((Get-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB).value) MB"
        Write-Warning "Current Memory for Plugin is : $((Get-Item WSMan:localhost\Plugin\microsoft.powershell\Quotas\MaxConcurrentCommandsPerShell).value) MB"
    }
    else
    {
        Write-Error "This Cmdlet requires Admin right. Please Elevate and try again."
    }

}
利用するときはこのようにします。 (例 : 2048 MBにするとき)
Set-PowerShellMemoryTuning -memory 2048

GitHub

https://github.com/guitarrapc/PowerShellUtil/blob/master/Set-PowerShellMemoryTuning/Set-PowerShellMemoryTuning.ps1