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

从ESXi读取VM时遭遇System.OutOfMemoryException问题求助

问题:从ESXi读取VM信息时频繁出现System.OutOfMemoryException错误

我在尝试使用以下C#函数从ESXi虚拟机管理程序读取VM信息时遇到了问题:

public static ReadHypervisorVmListRspn ReadHypervisorVmList(ReadHypervisorVmListRqst rqst)
{
    PowerShellClient ps = null;
    try
    {
        ps = new PowerShellClient();
        var rspn0 = ps.ExecuteScript("Set-ExecutionPolicy -Scope CurrentUser Unrestricted");
        var rspn = new PowerShellClient.ExecuteRspn();

        // Login to the hypervisor
        var cmd = "Connect-VIServer -Server " + rqst.LoginInfo.HostUrl + " -Protocol https -User " + rqst.LoginInfo.HostUserName +
           " -Password " + rqst.LoginInfo.HostPassword;
        rspn = ps.ExecuteScript(cmd);
        if (rspn.Response != "OK")
        {
            return new ReadHypervisorVmListRspn { Response = "Unable to connect !!" };
        }

        // Fetch VM information
        cmd = "Get-Vm | Select-Object Name,PowerState,NumCPU,MemoryGB,UsedSpaceGB,ProvisionedSpaceGB,@{N='GuestFullName';E={@($_.guest.OSFullName)}},@{N='PrivateIp';E={@($_ | Get-View).Guest.Ipaddress}},@{N='MacAddress';E={($_ | Get-NetworkAdapter).MacAddress}},@{N='Uuid'; E={($_ |Get-View).config.uuid}},@{N='UpTime';E={($_ |Get-View).Summary.QuickStats.Uptimeseconds}},@{n='HardDiskSizeGB'; e={(Get-HardDisk -VM $_ | Measure-Object -Sum CapacityGB).Sum}}";
        var rspn1 = ps.ExecuteScript(cmd, true, 1);
        if (rspn1.Response.Contains("An item with the same key has already"))
            rspn1 = ps.ExecuteScript(cmd);
        if (rspn1.Response != "OK")
        {             
            // Logout from the hypervisor
            cmd = "Disconnect-VIServer -Confirm:$false;";
            var rspn5 = ps.ExecuteScript(cmd);
            return new ReadHypervisorVmListRspn { Response = rspn1.Response };
        }

        // Logout from the hypervisor
        cmd = "Disconnect-VIServer -Confirm:$false;";
        var rspn4 = ps.ExecuteScript(cmd);

        // Process fetched VM information
        var rspn3 = rspn1.Output;
        var VmInfo = Json4.GetType<List<VmInfo>>(rspn3);
        var rspn2 = new ReadHypervisorVmListRspn()
        {
            Response = "OK",
            VmInfo = VmInfo
        };

        return rspn2;
    }
    catch (Exception ex)
    {           
        return new ReadHypervisorVmListRspn { Response = "Error" };
    }
    finally
    {
        // Clean up resources
        ps = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

该函数有时能成功运行,但多数情况下会出现System.OutOfMemoryException错误,错误信息如下:

18-02-2024 16:14:19 Get-NetworkAdapter  Exception of type 'System.OutOfMemoryException' was thrown. 
18-02-2024 16:14:20 Get-NetworkAdapter  Exception of type 'System.OutOfMemoryException' was thrown. 
18-02-2024 16:14:20 Get-HardDisk  Exception of type 'System.OutOfMemoryException' was thrown. 
18-02-2024 16:14:21 Get-View  Exception of type 'System.OutOfMemoryException' was thrown. 

我已尝试通过循环分批读取VM,但问题仍未解决,请问有什么可行的修复方法吗?


修复建议

1. 优化PowerShell脚本,减少重复资源调用

原脚本对每个VM多次调用Get-View、Get-NetworkAdapter、Get-HardDisk,会重复创建对象并持续占用内存。可以一次性获取所有VM的视图数据,直接从视图中提取所需信息,避免重复查询:

# 一次性加载所有VM的核心属性,避免多次子命令调用
Get-VM | Get-View -Property Name, Runtime.PowerState, Config.Hardware.NumCPU, Config.Hardware.MemoryMB, Guest.OSFullName, Guest.IpAddress, Config.Uuid, Summary.QuickStats.UptimeSeconds, Config.Hardware.Device | 
Select-Object `
    @{N='Name';E={$_.Name}},
    @{N='PowerState';E={$_.Runtime.PowerState}},
    @{N='NumCPU';E={$_.Config.Hardware.NumCPU}},
    @{N='MemoryGB';E={[math]::Round($_.Config.Hardware.MemoryMB/1024,2)}},
    @{N='UsedSpaceGB';E={[math]::Round((Get-VM -Id $_.MoRef).UsedSpaceGB,2)}},
    @{N='ProvisionedSpaceGB';E={[math]::Round((Get-VM -Id $_.MoRef).ProvisionedSpaceGB,2)}},
    @{N='GuestFullName';E={$_.Guest.OSFullName}},
    @{N='PrivateIp';E={$_.Guest.IpAddress -join ';'}},
    @{N='MacAddress';E={($_.Config.Hardware.Device | Where-Object {$_.GetType().Name -eq 'VirtualEthernetCard'}).MacAddress -join ';'}},
    @{N='Uuid';E={$_.Config.Uuid}},
    @{N='UpTime';E={$_.Summary.QuickStats.UptimeSeconds}},
    @{N='HardDiskSizeGB';E={[math]::Round((($_.Config.Hardware.Device | Where-Object {$_.GetType().Name -match 'VirtualDisk'}).CapacityInBytes | Measure-Object -Sum).Sum/1GB,2)}}

2. 改进PowerShell客户端资源管理

  • 移除手动调用GC.Collect()的代码,手动触发垃圾回收会打乱系统自动回收机制,反而可能加剧内存问题。
  • 确保PowerShellClient实现IDisposable接口,在finally块中调用ps.Dispose()释放资源,而非仅将对象设为null。
  • 检查PowerShellClient的ExecuteScript方法实现,确保每次执行后清理PowerShell运行空间,避免会话中累积未释放的对象。

3. 优化分批处理的资源释放逻辑

如果坚持分批读取,需确保每批处理完成后立即释放当前批次的输出对象,避免内存堆积:

// 示例:按批次处理VM
var batchSize = 10;
var vmNamesOutput = ps.ExecuteScript("Get-VM | Select-Object -ExpandProperty Name", true, 1).Output;
var vmNameList = Json4.GetType<List<string>>(vmNamesOutput);

foreach (var batch in vmNameList.Chunk(batchSize))
{
    var quotedNames = string.Join(',', batch.Select(n => $"'{n}'"));
    // 使用优化后的PowerShell查询脚本
    var cmd = $"Get-VM -Name {quotedNames} | Get-View -Property ... | Select-Object ...";
    var batchRspn = ps.ExecuteScript(cmd, true, 1);
    
    // 处理当前批次数据
    var batchVmInfo = Json4.GetType<List<VmInfo>>(batchRspn.Output);
    VmInfo.AddRange(batchVmInfo);
    
    // 显式清理当前批次资源
    batchRspn = null;
}

4. 避免一次性加载大体积JSON

如果VM数量较多,一次性返回所有VM的JSON字符串会占用大量内存。可以让PowerShell脚本逐行输出单条VM的JSON数据,在C#端流式读取并处理,而非一次性加载整个JSON到内存。

5. 移除重复的执行策略设置

Set-ExecutionPolicy -Scope CurrentUser Unrestricted只需执行一次,无需每次调用函数都重复执行。可以在应用启动时检查并设置执行策略,减少不必要的操作开销。


内容的提问来源于stack exchange,提问作者Maya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 11:22:31