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

PowerShell调用UserPrincipal.ChangePassword方法时出现内存不足错误

解决UserPrincipal.ChangePassword偶发“内存不足”跨域改密问题

问题背景

在Windows 10普通用户权限机器(32GB内存,已加入Domain A)上,使用System.DirectoryServices.AccountManagement命名空间的UserPrincipal.ChangePassword方法修改Domain B用户密码时,偶发System.OutOfMemoryException(错误提示:Insufficient memory to continue the execution of the program)。

核心特征:

  • $user对象每次可正常创建,仅调用ChangePassword时出错
  • 触发时机无规律,约1/5的机器会出现该问题,同一机器有时正常有时失败
  • 机器实际内存远未耗尽(仅使用约1/5)
  • 已尝试的无效方案:管理员权限运行脚本、用Start-Job隔离进程、重启后立即执行、手动调用[System.GC]::Collect()回收内存
  • 因权限/环境限制,无法使用SetPassword、AD模块Cmdlet、Ctrl+Alt+Del改密、NetUserChangePassword等其他方案,必须基于Kerberos协议的当前方案解决

可行解决方案

方案1:直接操作底层DirectoryEntry并显式释放资源

绕过UserPrincipal封装层,直接调用底层DirectoryEntry的改密方法,操作完成后强制释放所有资源,避免封装层可能的内存泄漏:

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
Add-Type -AssemblyName System.DirectoryServices

# 创建上下文
$context = New-Object System.DirectoryServices.AccountManagement.PrincipalContext(
    [System.DirectoryServices.AccountManagement.ContextType]::Domain,
    $DomainServer,
    $Username,
    $OldPassword
)

$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context, $Username)
$directoryEntry = $user.GetUnderlyingObject()

try {
    # 调用DirectoryEntry原生改密方法
    $directoryEntry.Invoke("ChangePassword", $OldPassword, $NewPassword)
    $directoryEntry.CommitChanges()
}
finally {
    # 显式释放所有资源
    $directoryEntry.Dispose()
    $user.Dispose()
    $context.Dispose()
}

方案2:修改PrincipalContext的上下文选项

创建PrincipalContext时添加特定选项,强制直接绑定域控制器并禁用不必要的缓存,减少内存累积:

Add-Type -AssemblyName System.DirectoryServices.AccountManagement

# 配置上下文选项:Kerberos认证+直接绑定域控制器
$contextOptions = [System.DirectoryServices.AccountManagement.ContextOptions]::Negotiate `
                  -bor [System.DirectoryServices.AccountManagement.ContextOptions]::Sealing `
                  -bor [System.DirectoryServices.AccountManagement.ContextOptions]::Signing `
                  -bor [System.DirectoryServices.AccountManagement.ContextOptions]::ServerBind

$context = New-Object System.DirectoryServices.AccountManagement.PrincipalContext(
    [System.DirectoryServices.AccountManagement.ContextType]::Domain,
    $DomainServer,
    $Username,
    $OldPassword,
    $contextOptions
)

$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context, $Username)
$user.ChangePassword($OldPassword, $NewPassword)

# 显式释放资源
$user.Dispose()
$context.Dispose()

ServerBind选项会跳过本地缓存直接绑定域控制器,避免缓存数据导致的内存异常;Negotiate/Sealing/Signing确保Kerberos认证的安全性。

方案3:拆分操作流程+分步释放对象

将创建上下文、获取用户、改密的步骤拆分,每步完成后立即释放不再使用的对象,结合强制GC回收:

Add-Type -AssemblyName System.DirectoryServices.AccountManagement

$context = $null
$user = $null

try {
    # 第一步:创建上下文并获取用户
    $context = New-Object System.DirectoryServices.AccountManagement.PrincipalContext(
        [System.DirectoryServices.AccountManagement.ContextType]::Domain,
        $DomainServer,
        $Username,
        $OldPassword
    )
    $user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context, $Username)

    # 第二步:执行改密
    $user.ChangePassword($OldPassword, $NewPassword)
}
finally {
    # 立即释放对象
    if ($user) { $user.Dispose() }
    if ($context) { $context.Dispose() }
    # 强制触发GC回收未释放的资源
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()
}

额外检查项

  • 确认Domain B的域控制器与当前机器的Kerberos认证通路正常,网络波动可能导致底层操作异常,伪装为内存错误
  • 升级机器的.NET Framework到最新兼容版本(Windows 10推荐.NET 4.8),旧版本的System.DirectoryServices组件可能存在内存泄漏bug

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:47:53