Sitecore PowerShell脚本内存占用过高无法释放致CM服务器崩溃求助
处理36K条Sitecore项目时PowerShell脚本内存泄漏导致CM服务器崩溃问题
运行以下处理36K条项目的Sitecore PowerShell脚本时,内存持续攀升最终导致CM服务器崩溃:
$refassets = @() $items = @() $items = Get-ChildItem -Path "master:/sitecore/content/Home/Products/" -Recurse $assetTemplateID = "{C2214D4C-883E-4B9C-B97A-C9CC00A1764E}" # Loop through each item foreach ($item in $items) { # Get related items using source template $sourceRefs = Get-ItemReference -Item $item | Where-Object { $_.TemplateID -eq $assetTemplateID } foreach ($ref in $sourceRefs) { $refAsset = [PSCustomObject]@{ L1 = $startingPath ProductName = $item.Name ProductPath = $item.Paths.FullPath ProductEntityID = $item.Fields["{B51F49BC-3628-4414-AB84-57A85DD4C243}"] ProductGUID = $item.ID ProductLevel = $item.Fields["productLevel"] AssetID = $ref.Fields["AssetID"] AssetName = $ref.Name AssetFullPath = $ref.Paths.FullPath AssetGUID = $ref.ID ResourceType = $resourceType Unpublished = $ref.Fields["__Never publish"].Value } $refassets.Add($refAsset) } } "Completed Processing" $refassets | Show-ListView $refassets.Dispose() $items.Dispose() Remove-Variable Name * [System.GC]::Collect()
已尝试以下方案但无效果:
- 使用
Remove-Variable、Dispose()及[System.GC]::Collect(); - 设置PowerShell ISE会话在脚本执行后销毁;
- 等待一小时观察内存是否释放。
问题根源
- 一次性加载36K条Sitecore Item到内存,每个Item对象持有数据库连接、字段缓存等资源,无法被及时回收;
- 将所有关联的Asset结果存储在
$refassets数组中,随着数据量增长,内存占用持续飙升; Get-ItemReference会加载所有关联项,未做任何过滤或分批处理,进一步加剧内存压力。
优化解决方案
1. 分批加载处理Item
避免一次性递归加载所有Item,改用分批查询的方式,每次处理固定数量的Item:
$batchSize = 100 $page = 0 $assetTemplateID = "{C2214D4C-883E-4B9C-B97A-C9CC00A1764E}" $basePath = "master:/sitecore/content/Home/Products/" do { # 分批获取Item,避免一次性加载全部 $items = Get-ChildItem -Path $basePath -Recurse | Select-Object -Skip ($page * $batchSize) -First $batchSize if ($items.Count -eq 0) { break } foreach ($item in $items) { $sourceRefs = Get-ItemReference -Item $item | Where-Object { $_.TemplateID -eq $assetTemplateID } foreach ($ref in $sourceRefs) { # 直接输出结果到管道,不存储到数组 [PSCustomObject]@{ L1 = $startingPath ProductName = $item.Name ProductPath = $item.Paths.FullPath ProductEntityID = $item.Fields["{B51F49BC-3628-4414-AB84-57A85DD4C243}"] ProductGUID = $item.ID ProductLevel = $item.Fields["productLevel"] AssetID = $ref.Fields["AssetID"] AssetName = $ref.Name AssetFullPath = $ref.Paths.FullPath AssetGUID = $ref.ID ResourceType = $resourceType Unpublished = $ref.Fields["__Never publish"].Value } # 及时释放Item引用 $ref.Dispose() } $item.Dispose() } $page++ # 强制GC回收 [System.GC]::Collect() } while ($true) | Show-ListView
2. 避免使用数组存储结果
直接通过管道将结果输出到Show-ListView或Export-Csv,不需要将所有数据保存在内存中:
$assetTemplateID = "{C2214D4C-883E-4B9C-B97A-C9CC00A1764E}" # 替换原数组存储逻辑,直接输出到管道 Get-ChildItem -Path "master:/sitecore/content/Home/Products/" -Recurse | ForEach-Object { $item = $_ Get-ItemReference -Item $item | Where-Object { $_.TemplateID -eq $assetTemplateID } | ForEach-Object { $ref = $_ [PSCustomObject]@{ L1 = $startingPath ProductName = $item.Name ProductPath = $item.Paths.FullPath ProductEntityID = $item.Fields["{B51F49BC-3628-4414-AB84-57A85DD4C243}"] ProductGUID = $item.ID ProductLevel = $item.Fields["productLevel"] AssetID = $ref.Fields["AssetID"] AssetName = $ref.Name AssetFullPath = $ref.Paths.FullPath AssetGUID = $ref.ID ResourceType = $resourceType Unpublished = $ref.Fields["__Never publish"].Value } $ref.Dispose() } $item.Dispose() [System.GC]::Collect() } | Show-ListView
3. 高效查询关联项
改用Sitecore Fast Query直接获取符合条件的关联项,减少不必要的对象加载:
# 使用Fast Query直接查询关联的Asset项 $query = "fast://*[@@templateid='{C2214D4C-883E-4B9C-B97A-C9CC00A1764E}' and @@ancestor::*[@@path='/sitecore/content/Home/Products']]" Get-Item -Path $query | ForEach-Object { $ref = $_ $productItem = $ref.Axes.SelectSingleItem("ancestor-or-self::*[@@path='/sitecore/content/Home/Products']/..") [PSCustomObject]@{ L1 = $startingPath ProductName = $productItem.Name ProductPath = $productItem.Paths.FullPath ProductEntityID = $productItem.Fields["{B51F49BC-3628-4414-AB84-57A85DD4C243}"] ProductGUID = $productItem.ID ProductLevel = $productItem.Fields["productLevel"] AssetID = $ref.Fields["AssetID"] AssetName = $ref.Name AssetFullPath = $ref.Paths.FullPath AssetGUID = $ref.ID ResourceType = $resourceType Unpublished = $ref.Fields["__Never publish"].Value } $productItem.Dispose() $ref.Dispose() } | Show-ListView
4. 脚本执行环境优化
- 使用Sitecore PowerShell Extensions的Background Script功能,在后台执行脚本,避免占用CM服务器前端资源;
- 在脚本开头添加
$PSMaximumMemoryPerShellMB = 2048(根据服务器配置调整数值),限制单会话内存配额。
内容的提问来源于stack exchange,提问作者Madhu
相关产品推荐
相关产品推荐

