You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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会话在脚本执行后销毁;
  • 等待一小时观察内存是否释放。

问题根源

  1. 一次性加载36K条Sitecore Item到内存,每个Item对象持有数据库连接、字段缓存等资源,无法被及时回收;
  2. 将所有关联的Asset结果存储在$refassets数组中,随着数据量增长,内存占用持续飙升;
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:43:35