Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#启动进程会泄漏内存,即使已终止并已处理(在Linux上)_C#_.net_Linux_.net Core_Memory Leaks - Fatal编程技术网

C#启动进程会泄漏内存,即使已终止并已处理(在Linux上)

C#启动进程会泄漏内存,即使已终止并已处理(在Linux上),c#,.net,linux,.net-core,memory-leaks,C#,.net,Linux,.net Core,Memory Leaks,注意:根据测试(见下面的编辑),这只发生在Linux机器上 我有一个ASP.NET核心Blazor应用程序(使用服务器端托管模型)在Raspberry Pi上运行。该应用程序的部分功能是根据上次与系统交互的时间来调暗/调亮屏幕。为此,我每隔1秒左右生成一个终端子进程来运行xprintidle,解析其输出,并相应地执行操作 我使用DataDog进行监控,直到系统崩溃,我的内存一直在泄漏(耗尽所有内存需要几天时间,但最终还是会发生): 我已经指出,以下方法会泄漏内存-如果我跳过调用它并使用一些恒定

注意:根据测试(见下面的编辑),这只发生在Linux机器上

我有一个ASP.NET核心Blazor应用程序(使用服务器端托管模型)在Raspberry Pi上运行。该应用程序的部分功能是根据上次与系统交互的时间来调暗/调亮屏幕。为此,我每隔1秒左右生成一个终端子进程来运行
xprintidle
,解析其输出,并相应地执行操作

我使用DataDog进行监控,直到系统崩溃,我的内存一直在泄漏(耗尽所有内存需要几天时间,但最终还是会发生):

我已经指出,以下方法会泄漏内存-如果我跳过调用它并使用一些恒定的时间跨度,内存不会泄漏: 我有以下代码来执行此操作:

//注意,这段代码中有些部分甚至是不需要的——我只是在尝试解决这个问题
公共异步任务ExecuteAndWaitAsync(字符串命令,bool asRoot,CancellationToken CancellationToken=default)
{
使用processprc=CreateNewProcess(命令,asRoot);
//我们需要重定向stdstream来读取它们
prc.StartInfo.RedirectStandardOutput=true;
prc.StartInfo.RedirectStandardError=true;
//开始这个过程
_LogTrace(“启动流程”);
使用任务waitForExitTask=WaitForExitAsync(prc,cancellationToken);
prc.Start();
//读流
string[]streamResults=await Task.WhenAll(prc.StandardOutput.ReadToEndAsync(),prc.StandardError.ReadToEndAsync()).ConfigureAwait(false);
//等待它完全退出,但不要超过半秒
//这可以防止在进程已完成时挂起,但需要很长时间才能完全关闭
wait Task.whenay(waitForExitTask,Task.Delay(500,cancellationToken)).ConfigureWait(false);
//若进程仍然并没有退出,强制终止它
如果(!prc.已退出)
prc.Kill(true);//使用try-catch方法而不是HasExited-check进行此操作没有区别
返回新的TerminalResult(streamResults[0],streamResults[1]);
}
公共任务WaitForExitAsync(进程进程,CancellationToken CancellationToken=default)
{
TaskCompletionSource tcs=新的TaskCompletionSource();
IDisposable tokenRegistration=null;
EventHandler回调=null;
tokenRegistration=cancellationToken.Register(()=>
{
注销();
tcs.TrySetCanceled(取消令牌);
});
回调=(发送方,参数)=>
{
注销();
tcs.TrySetResult(process.ExitCode);
};
process.Exited+=回调;
process.EnableRaisingEvents=true;
作废注销()
{
锁(tcs)
{
if(令牌注册==null)
返回;
process.EnableRaisingEvents=false;
process.Exited-=回调;
令牌注册?.Dispose();
tokenRegistration=null;
}
}
返回tcs.Task;
}
私有进程CreateNewProcess(字符串命令,bool asRoot)
{
_LogDebug(“创建进程:{Command}”,Command);
过程prc=新过程();
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
string escapedCommand=command.Replace(“\”,“\ \ \”);
//如果是根,就做它
if(asRoot)
prc.StartInfo=newprocessstartinfo(“/bin/bash”,$”-c \“sudo{escapedCommand}\”);
//如果不是以root用户身份,我们需要以当前用户身份打开它
//如果进程以root用户身份运行,那么它仍然可以以root用户身份运行
其他的
prc.StartInfo=newprocessstartinfo(“/bin/bash”,$”-c\“{escapedCommand}\”);
}
else if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
prc.StartInfo=newprocessstartinfo(“CMD.exe”,$”/C{command});
if(asRoot)
prc.StartInfo.Verb=“runas”;
}
其他的
抛出新的PlatformNotSupportedException($“{nameof(ExecuteAndWaitAsync)}仅在Windows和Linux平台上受支持。”);
prc.StartInfo.UseShellExecute=false;
prc.StartInfo.CreateNoWindow=true;
如果(_log.IsEnabled(LogLevel.Trace))
{
_LogTrace(“exec:{FileName}{Args}”、prc.StartInfo.FileName、prc.StartInfo.Arguments);
_LogTrace(“exec:as root={AsRoot}”,AsRoot);
}
返回中国;
}
我花了很多时间(在几个月的时间里——从字面上说)尝试各种更改来解决这个问题——
WaitForExitAsync
被彻底检查,尝试了不同的处理方法。我尝试定期调用GC.Collect()。还尝试使用服务器和工作站GC模式运行应用程序

正如我前面提到的,我非常确定是这段代码泄漏——如果我不调用
ExecuteAndWaitAsync
,就不会有内存泄漏。结果类也不是由调用方存储的-它只是解析一个值并立即使用它:

公共异步任务GetSystemIdleTimeAsync(CancellationToken CancellationToken=default)
{
ThrowIfNotLinux();
常量字符串prc=“xprintidle”;
TerminalResult=await_terminal.ExecuteAndWaitAsync(prc,false,cancellationToken).ConfigureAwait(false);
if(result.HasErrors | | |!int.TryParse(result.Output,out int idleMs))
抛出新的InvalidOperationException($“{prc}返回了无效数据。”);
返回TimeSpan.From毫秒(idleMs);
}
私有静态void ThrowIfNotLinux()
{
if(!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
抛出新的PlatformNotSupportedException($“{nameof(backgroundControl)}仅在Linux系统上可用。”);
}
我错过什么了吗?是进程类泄漏,还是我读取输出的方式

编辑:正如评论中的人所问,我创建了最小可运行代码,基本上在单个类中获取所有相关方法,并在循环中执行。代码作为要点提供:
我两个都跑了