使用记事本的示例

使用记事本的示例

我的系统运行时占用大量已分配内存(8GB RAM + 2GB 页面文件中 85% 的内存已分配)。物理使用率约为 65%。

我如何确定哪些进程分配了大部分已提交的内存?我知道内存可以在进程之间共享。到目前为止,我使用 VMMap 来显示已提交的内存,但这是基于每个进程的,不考虑页面文件支持的部分

在此处输入图片描述

在此处输入图片描述

答案1

PowerShell 解决方案

1.获取使用虚拟内存最多的前 10 个进程

Get-Process | Sort PagedMemorySize-Desc | Select Name, PagedMemorySize, VirtualMemorySize -First 10

输出示例

Name                  VirtualMemorySize PagedMemorySize
----                  ----------------- ---------------
UrBackupClientBackend         685735936       548347904
explorer                     1529909248       478908416
Microsoft.Photos             1303465984       433094656
MBAMService                   661987328       228876288
MicrosoftEdgeCP               894496768       219799552
MsMpEng                       667783168       205774848
MicrosoftEdgeCP               874590208       202584064
mstsc                         440627200       185860096
javaw                         886177792       185556992
MicrosoftEdgeCP               802746368       146792448

2.获取所有已提交的虚拟内存的总和

Get-WmiObject win32_operatingsystem | Select @{L='commit';E={($_.totalvirtualmemorysize - $_.freevirtualmemory)*1KB/1GB}} 

输出示例

commit
------
4.56205749511719

支持资源

答案2

进程探索器 可以显示每个进程的信息:

图像

以下是如何在 Process Explorer 中获取上述屏幕:

  • 点击菜单视图 > 显示下方窗格
  • 点击菜单视图 > 下方窗格视图 > DLL
  • 点击菜单视图 > 显示未命名的句柄和映射
  • 单击上部窗格中的某个进程
  • 右键单击下方窗格的标题并选择选择列...
  • 在 DLL 选项卡中,勾选映射大小映射类型
  • 单击“确定”

进程黑客 可以类似地显示此信息,选择并双击某个进程后,在“句柄”选项卡中取消选中隐藏未命名的手柄

答案3

在 Process Explorer 的进程列表中,“Private Bytes”列显示每个进程对提交费用的贡献。无需查看下方窗格视图。

确保以管理员身份运行 Process Explorer。

任务管理器在“详细信息”选项卡的“提交大小”列中显示相同的信息。

请注意,任务管理器在“内存(私有工作集)”列中显示的内容并不是同一个东西,尽管它使用了“私有”一词。它显示的是每个进程的提交费用的子集,而该子集恰好位于该进程的 RAM 中。

Windows 内部原理,总提交费用的贡献者是:

  • 每个流程中私有承诺的 vas
  • 页面文件支持的映射 vas(不会显示在进程的“私有字节”中)
  • 映射 vas 的写时复制区域
  • 非分页池和分页池
  • 其他未明确由文件支持的内核空间分配(例如,驱动程序或 ntoskrnl.exe 中的可分页代码不算,因为它由相应的可执行文件支持)
  • 内核堆栈 - 每个线程都有一个
  • 页表
  • 尚未实际分配但已提交的 vas 的页表空间
  • “地址窗口扩展”(AWE)分配

Windows 内部原理更详细地介绍了这些内容是什么,以及为什么每个内容都计入系统范围的提交费用。不幸的是,没有计数器虚拟的许多这些东西的大小,这就是提交电荷的意义所在。RAMmap 显示其中一些的物理大小,但没有显示虚拟大小。

答案4

因此,从我所掌握的信息来看,它vmmap似乎是在做自己的计算(它确实安装了内核模式驱动程序才能正常运行),而不是使用中的 API win32_process

您可以使用 arg 从 CLI 重新计算它在 GUI 中所做的事情,outfile <filename>该 arg 会生成一个.mmp仅 XML 的文件。

实际上,调查提交大小的贡献似乎不是一件容易的事,因为“可共享”可能是 1 个进程的问题,也可能是多个进程的问题。私有数据最常发生泄漏,但如果您只关心大小,则必须考虑图像、地址窗口扩展、AWE 等。

vmmap是一个很棒的工具,但它似乎是为分析单个过程的性能而编写的。

Mark Russinovich (sysinternals 的创建者) 在 YouTube 上的演讲非常有启发性:揭秘内存管理的奥秘,作者:Mark Russinovich(第一部分)。正如其他人提到的,Windows 内部原理书和Windows 故障排除书籍(也是 Russinovich 所著)也很有帮助。

令人沮丧的是这些信息无法在操作系统中轻易获得。

使用记事本的示例

此示例将从记事本中导出单个快照vmmap并将其读回。然后它将某些区域的值相加Commit。这个数字似乎与 GUI 上显示的数字相近。

vmmap_解析.ps1

首先打开记事本。您也可以打开vmmap并检查该过程以进行比较。

function New-VmMapLogFile {
#Requires -RunAsAdministrator
[CmdletBinding()]
param(
  [Parameter(Mandatory=$True,ValueFromPipelineByPropertyName=$True)]
  [Alias('id')]
  [int32[]]
  $Ids,
  
  [Parameter(Mandatory=$False)]
  [ValidateNotNullOrEmpty()]
  [string]
  $DestinationPath = (Join-Path $PWD.Path "vmmap_$(get-date -f 'yyyyMMdd_HHmmss')"),
  
  [Parameter(Mandatory=$False)]
  [ValidateNotNullOrEmpty()]
  [string]
  $VmMapPath = 'c:\sysinternals\vmmap.exe'
)
BEGIN {
  if(-not (Test-Path -Path $DestinationPath)) {
    New-Item -Path $DestinationPath -ItemType 'container' -ea 1 | out-null
  }
  if(-not (Test-Path -Path $VmMapPath -PathType 'leaf')) {
    throw "vmmap missing"
  }
}
PROCESS {
  foreach($id in $Ids) {
    $proc = $null
    $proc = Get-Process -Id $id -ea 0
    if(-not $proc) {
      write-warning "cannot find PID $($id)"
      continue
    }
    [System.IO.FileInfo]$outputfile = Join-Path $DestinationPath "$($id).mmp"
    write-verbose "creating $($outputfile.FullName)"
    $vmmapProcArgs = @{
        Wait = $True
        FilePath = $VmMapPath
        ArgumentList = @(
            '-accepteula',
            '-p',$id,
            'outputfile',$outputfile.FullName
        )
        WindowStyle = 'hidden'
    }
    Start-Process @vmmapProcArgs
    if(@(0) -notcontains $LASTEXITCODE) {
      write-warning "vmmap PID $($id) returned: $($LASTEXITCODE)"
    }
  }
}}

function Import-VMMapLogFiles {
param(
  [Parameter(Mandatory=$True)]
  [ValidateNotNullOrEmpty()]
  [ValidateScript({ Test-Path -Path $_ -PathType 'container' })]
  [string]
  $DirectoryPath
)
    write-verbose "Checking dir $($DirectoryPath) for *.mmp files"
    $mmpFiles = @(Get-ChildItem -Path $DirectoryPath -File -Filter '*.mmp')
    write-verbose "$($mmpFiles.Count) files found"
    foreach($mmpFile in $mmpFiles) {
        $objProps = [ordered]@{
            PID = $vmmap.root.PID
            Process = $vmmap.root.Process
        }
        # read XML file
        [xml]$vmmap = Get-Content -Path $mmpFile.FullName -Raw
        $regions = @($vmmap.root.Snapshots.Snapshot.MemoryRegions.Region)
        $regionByType = $regions | Group-Object -Property 'Type'
        
        # examine regions
        $totalCommitKb = 0
        foreach($r in $regionByType) {
            $keyPrefix = ($r.Name.ToLower() -replace '\s+','_')

            # filter regions
            $validRegions = @()
            switch($r.Name) {
                'Private Data' {
                    # 4096 regions only
                    $validRegions = @($R.Group | Where-Object { $_.Size - 4096 })
                }
                default {
                    $validRegions = @($R.Group)
                }
            }

            # commited sum
            $commitKeyRegex = 'image|shareable|private_data|heap|stack'
            $commitRegions = @($validRegions | where-object { $_.Commit -gt 0 })
            $commitBytes = ($commitRegions | Select-Object -ExpandProperty Commit | Measure-Object -Sum | Select-Object -ExpandProperty Sum)
            $commitKb = $commitBytes / 1KB
            $commitRounded = [math]::Round($commitKb, 2)
            if($keyPrefix -match $commitKeyRegex) {
                $totalCommitKb += $commitRounded
            }

            # size sum
            $sizeBytes = 0
            $sizeBytes = $validRegions | Select-Object -ExpandProperty Size | Measure-Object -Sum | Select-Object -ExpandProperty Sum
            $sizeKb = $sizeBytes / 1KB
            $sizeKbRounded = [math]::Round($sizeKb, 2)
            
            # add properties
            $objProps["$($keyPrefix)_kb"] = $sizeKbRounded
            $objProps["$($keyPrefix)_commit_kb"] = $commitRounded
        }
        $objProps['commit_total_kb'] = $totalCommitKb
        [pscustomobject]$objProps
    }
}

$tmpDir = Join-Path $PSScriptRoot 'notepad'
Remove-Item -Path $tmpDir -recurse -force -ea 0 | out-null

get-process -name 'notepad' -ea 1 | New-VmMapLogFile -DestinationPath $tmpDir
Import-VMMapLogFiles -Verbose -DirectoryPath $tmpDir

结果

来自 GUI 虚拟机映射图形用户界面

脚本输出

PID                           : 15320
Process                       : "C:\Windows\System32\notepad.exe"
free_kb                       : 135287120640
free_commit_kb                : 0
private_data_kb               : 4238760
private_data_commit_kb        : 316
unusable_kb                   : 2904
unusable_commit_kb            : 0
thread_stack_kb               : 512
thread_stack_commit_kb        : 80
heap_(shareable)_kb           : 64
heap_(shareable)_commit_kb    : 64
shareable_kb                  : 2147510224
shareable_commit_kb           : 46412
mapped_file_kb                : 23040
mapped_file_commit_kb         : 23040
heap_(private_data)_kb        : 4516
heap_(private_data)_commit_kb : 636
image_(aslr)_kb               : 52720
image_(aslr)_commit_kb        : 52720
commit_total_kb               : 123268

相关内容