当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 解决 PowerShell 内存挑战

作者:精品下载站 日期:2024-12-14 08:01:46 浏览:18 分类:玩电脑

解决 PowerShell 内存挑战


我希望您能尝试一下有关报告内存使用情况的 Iron Scripter PowerShell 挑战。基本挑战是找到特定进程或服务正在使用的工作集内存的总百分比。这是我的处理方法,我通常免责声明,我的解决方案不是唯一的,也不应该被认为是权威的。尽管如此,我还是希望您能掌握 PowerShell 脚本编写技巧或技术。

使用 Get-CimInstance

我的第一种方法使用 Get-CimInstance,主要是因为它可以从远程计算机获取信息。我将要执行的代码是可以在 PowerShell 提示符下运行的命令。一旦这些命令起作用并产生我想要的结果,我就可以将它们包装到一个函数中。

首先,我需要了解当前正在使用多少内存。

$computername = $env:COMPUTERNAME
$os = Get-CimInstance win32_operatingsystem -Property TotalVisibleMemorySize,FreePhysicalMemory -Computername $computername
$inUseMemory = ($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)*1KB

让我指出将 -Property 参数与 Get-CimInstance 一起使用。这是您可以做的一件小事,可能会带来积极的性能提升。从 CIM 存储库获取信息非常类似于 SQL 查询。最好、最快的查询是您只选择所需属性的查询。因为我只需要两个内存属性,所以这就是我所要求的。根据经验和之前的研究,我还知道这些值以 KB 为单位。将值乘以 1KB 会将值转换为字节。

使用此值,我可以查询特定服务并创建自定义输出,以计算正在使用的总内存中的工作集内存百分比。

Get-CimInstance win32_process -filter "name='sqlservr.exe'" |
Select-Object -Property ProcessID,Name,HandleCount,WorkingSetSize,
@{Name="PctUsedMemory";Expression = {($_.WorkingSetSize/$InUseMemory) * 100}}

[玩转系统] 解决 PowerShell 内存挑战

将其转变为简单的 PowerShell 函数并不需要太多时间。

但与此同时,我会将这个概念扩展到除系统空闲之外的所有进程。

Get-CimInstance -class win32_process -computername $computername -filter "Name<>'systemidleprocess'" |
 Group-Object -property Name |
 ForEach-Object {
 $stat = $_.Group | Measure-Object -Property WorkingSetSize -sum
 $_ | Select-Object -Property Name,Count,@{Name="TotalWS";Expression={$stat.sum}},
 @{Name="PctUsedMemory";Expression={[math]::Round(($stat.sum/$InUseMemory)*100,2)}}
 } | Sort-Object -property PctUsedMemory -Descending|
 Select-Object -first 10 |
 Format-Table

此代码还使用我在开始时检索到的 $InUseMemory 值。流程实例根据“名称”属性进行分组。这是因为我可能有 10 个 svchost 进程,并且我想将它们视为一项。

然后使用 ForEach-Object 枚举分组的对象。对于组中的每个项目,我都会测量该组的总工作集大小。

$stat = $_.Group | Measure-Object -Property WorkingSetSize -sum

然后,我使用 Select-Object 创建一个自定义对象来定义新属性。结果通过管道传输到 Sort-Object,以按降序对自定义 PctUsedMemory 属性进行排序。排序后的结果通过管道传输到 Select-Object 以获取前 10 个结果。最后,为了使其在屏幕上美观,我将结果格式化为表格。

[玩转系统] 解决 PowerShell 内存挑战

如果我要把它变成一个函数,我很可能会创建一个真正的自定义对象,而不是依赖 Select-Object。

$computername = $env:COMPUTERNAME
$os = Get-CimInstance win32_operatingsystem -Property TotalVisibleMemorySize,FreePhysicalMemory -Computername $computername
$inUseMemory = ($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)* 1KB
$grouped = Get-CimInstance win32_process -computername $computername -filter "Name <>'system idle process'" |
Group-Object -property Name

$results = ForEach ($item in $Grouped) {
    $stat = $item.Group | Measure-Object -Property WorkingSetSize -Sum
    [pscustomobject]@{
        PSTypeName    = "ProcessWSPercent"
        Name          = $item.Name
        Count         = $item.count
        TotalWS       = $stat.sum
        PctUsedMemory = [math]::Round(($stat.sum / $InUseMemory) * 100, 2)
    }
}

$results | Sort-Object -property PctUsedMemory -Descending |
Select-Object -first 10

我认为这种方法在脚本文件中更容易阅读。如果我愿意,我可以创建一个自定义 format.ps1xml 文件来格式化结果。也许以MB而不是字节为单位显示TotalWS,这就是我现在所拥有的。

[玩转系统] 解决 PowerShell 内存挑战

使用获取进程

这是使用 Get-Process 的变体。在此版本中,我将从所有进程中获取总工作集值。

$ps = Get-Process
$totalWS = ($ps | Measure-Object -Property WS -Sum).sum

现在我可以过滤、选择、排序并选择我想查看的内容。

$ps | Where-Object {$_.name -notmatch "^(system|idle)$"} |
Select-Object -property Name,WS,@{Name="PctTotalWS";Expression = {($_.ws/$totalWS)*100}} |
Sort-Object -Property PctTotalWS -Descending |
Select-Object -first 10

[玩转系统] 解决 PowerShell 内存挑战

此变体显示每个过程的结果。我可以按照与之前类似的方式对它们进行分组。

$ps | Where-Object { $_.name -notmatch "^(system|idle)$" } |
Group-Object -Property name |
ForEach-Object {
    $wsSum = ($_.group | Measure-Object -Property WS -Sum).sum
    [PSCustomObject]@{
        Name       = $_.Name
        Count      = $_.count
        TotalWS    = $wsSum
        PctTotalWS = [math]::round(($wsSum / $totalWS) * 100, 4)
    }
} | Sort-Object -Property PctTotalWS -Descending | Select-Object -First 5

[玩转系统] 解决 PowerShell 内存挑战

同样,将其转变为易于使用的 PowerShell 函数并不需要太多时间。

使用性能计数器

我解决这个问题的最后一种方法是使用性能计数器。我将使用可能有多个实例的单个进程。

$name = "firefox"
$Counters = (Get-Counter "\process(firefox*)\Working Set").CounterSamples
$total = ($counters | Measure-Object -property CookedValue -sum).Sum
$committed = (Get-Counter "\memory\committed bytes").Countersamples[0].CookedValue
$pct = ($total/$committed)*100

您会注意到,我使用索引 [0] 来获取提交的字节值,即使您运行 Get-Counter 命令您也只会看到一个结果。 Get-Counter 有一个小怪癖,它返回一个带有空第二个值的数组。使用这些值,我可以创建一个结果对象。

[pscustomobject]@{
        ProcessName = $name
        ProcessCount = $counters.count
        TotalWS = [math]::round($total / 1mb, 4)
        PctCommitted = [math]::round($pct, 4)
}

[玩转系统] 解决 PowerShell 内存挑战

我将把这个概念扩展到所有流程,再次对类似的流程进行分组。

[pscustomobject]@{
        ProcessName = $name
        ProcessCount = $counters.count
        TotalWS = [math]::round($total / 1mb, 4)
        PctCommitted = [math]::round($pct, 4)
}

我的代码使用正则表达式来过滤掉选择的性能计数器结果。其余代码应该看起来很熟悉。

$data = foreach ($item in $grouped) {
          $total = ($item.group | Measure-Object -Property CookedValue -Sum).Sum
          $pct = ($total / $committed) * 100
        
          [pscustomobject]@{
            ProcessName  = $item.Name
            ProcessCount = $item.count
            TotalWS      = [math]::round($total / 1mb, 4)
            PctCommitted = [math]::round($pct, 4)
          }
        }
        
$data | Sort-Object TotalWS -Descending | Select-Object -first 10

[玩转系统] 解决 PowerShell 内存挑战

此代码应在 Windows 机器人 Windows PowerShell 和 PowerShell 7.1 下运行。

服务

最后,让我们看看如何从服务角度解决这个问题。正在运行的服务是一个正在运行的进程,因此我应该能够使用我已经向您展示的大部分代码。我将重新运行代码以获取 $InUseMemory 值。

$computername = $env:COMPUTERNAME
$os = Get-CimInstance win32_operatingsystem -Property TotalVisibleMemorySize,FreePhysicalMemory -Computername $computername
$inUseMemory = ($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)* 1KB

我将使用 Get-CimInstance 查询 Win32_Service 类,因为它将包含关联的进程 ID。因此,对于每个服务,我可以获得相应的流程并构建自定义对象。

Get-CimInstance -classname win32_service -Filter "state = 'running'" -Property Name, ProcessID -ComputerName $computername |
ForEach-Object -Begin {
    #define a hashtable of parameters to splat to Get-CimInstance in the process block
    $splat = @{
        ClassName    = "Win32_process"
        filter       = ""
        Property     = "Name", "ProcessID", "WorkingSetSize"
        Computername = $Computername
    }
} -Process {
    #update the filter
    $splat.filter = "processid=$($_.processid)"
    $proc = Get-CimInstance @splat
    [pscustomobject]@{
        PSTypeName    = "svcMemoryDetail"
        ServiceName   = $_.Name
        ProcessName   = $proc.Name
        ProcessID     = $proc.ProcessId
        TotalWS       = $proc.WorkingSetSize
         PctUsedMemory = [math]::Round(($proc.WorkingSetSize / $inUseMemory) * 100, 4)
    }
} | Sort-Object PctUsedMemory -Descending | Select-Object -First 10 | Format-Table

[玩转系统] 解决 PowerShell 内存挑战

如果我要继续并围绕此创建一个函数,我还会创建一个自定义 format.ps1xml 文件来呈现一个漂亮的表格。

包起来

如果您关注我一段时间,您应该会认识到熟悉的工作流程。

  • 编写在提示符下交互式运行的代码。
  • 创建一个包含将对象写入管道的代码的函数。
  • 添加自定义类型和格式更新,以便轻松处理命令结果。

如果您在控制台代码中使用变量,就像我对 $Computername 所做的那样,那么将它们用作函数参数并不需要太多努力。

诚然,这篇文章涉及很多内容,所以如果您有疑问或需要一些澄清,请不要害羞。很可能其他人也这样做。否则,我希望您自己尝试一些代码,看看它是如何工作的。我还希望您尝试一下 Iron Scripter 挑战。所有级别都有测试,并且没有截止日期。

祝你好运。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯