Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/xamarin/3.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
Function 函数中的日志文件变量会耗尽所有内存_Function_Powershell_Scope - Fatal编程技术网

Function 函数中的日志文件变量会耗尽所有内存

Function 函数中的日志文件变量会耗尽所有内存,function,powershell,scope,Function,Powershell,Scope,我有一个简单的函数,它在一个脚本中多次使用,该脚本遍历一个目录并检查其中文件的年代 function log($Message) { $logFilePath = 'C:\logPath\myLog.txt' $date = Get-Date -Format 'yyyyMMddHHmmss' $logMessage = "{0}_{1}" -f $date,$Message if(Test-Path -Path $logFilePath) { $logFileConte

我有一个简单的函数,它在一个脚本中多次使用,该脚本遍历一个目录并检查其中文件的年代

function log($Message) {
  $logFilePath = 'C:\logPath\myLog.txt'

  $date = Get-Date -Format 'yyyyMMddHHmmss'
  $logMessage = "{0}_{1}" -f $date,$Message
  if(Test-Path -Path $logFilePath) {
    $logFileContent = Get-Content -Path $logFilePath
  } else {
    $logFileContent = ''
  }
  $logMessage,$logFileContent | Out-File -FilePath $logFilePath
}

我发现这会吃掉所有的内存。我不明白为什么。我认为一旦函数运行,函数中变量的作用域就会被破坏。我通过添加
Remove变量logMessage、logFileContent、logFilePath、,日期
到函数的最后,但我想知道如何解决这个ram问题,以及为什么函数中的变量不会自动销毁。

我只想排除ram的使用是由文件的前置引起的。您是否尝试过不将日志内容存储在变量中?i、 e

$logMessage,(获取内容-路径$logFilePath)|输出文件-路径$logFilePath

编辑5/8/20-结果表明,预结束(有效完成时)并不像我想象的那么慢-它的顺序与使用
-Append
的顺序相同。然而,它又长又难看,但是如果您真的需要在文件前加前缀,这是一种方法

我稍微修改了OP的代码,以便自动插入新行

函数日志前置{
param(
$content,
$filePath='C:\temp\myLogP.txt'
)
$file=获取项$filePath
如果(!$file.exists){
写入错误“$文件不存在”;
返回;
}
$filepath=$file.fullname;
$tmptoken=(获取位置).path+“\\u tmpfile”+$file.name;
详细写入“$tmptoken已创建为缓冲区”;
$tfs=[System.io.file]::创建($tmptoken);
$fs=[System.IO.File]::Open($File.fullname,[System.IO.FileMode]::Open,[System.IO.FileAccess]::ReadWrite);
试一试{
$date=获取日期-格式为“yyyyMMddHHmmss”
$logMessage=“{0}{1}`r`n”-f$date,$content
$msg=$logMessage.tochararray();
$tfs.write($msg,0,$msg.length);
$fs.position=0;
$fs.copyTo($tfs);
}
抓住{
编写详细的$\异常.Message;
}
最后{
$tfs.close();
$fs.close();
如果($error.count-等式0){
详细写入(“更新$filepath”);
[System.io.File]::删除($filepath);
[System.io.file]::Move($tmptoken,$filepath);
}
否则{
$error.clear();
[System.io.file]::删除($tmptoken);
}
}
}
这是我最初的答案,说明了如何使用秒表测试计时

当您在日志文件前加前缀时,您正在将整个日志文件读入内存,然后将其写回

您真的应该使用append-这将使脚本运行得更快

函数日志($Message){
$logFilePath='C:\logPath\myLog.txt'
$date=获取日期-格式为“yyyyMMddHHmmss”
$logMessage=“{0}{1}”-f$date,$Message
$logMessage |输出文件-文件路径$logFilePath-追加
}
编辑:为了让您相信在日志文件前加前缀是个坏主意,您可以在自己的系统上进行以下测试:

function logAppend($Message) {
  $logFilePath = 'C:\temp\myLogA.txt'

  $date = Get-Date -Format 'yyyyMMddHHmmss'
  $logMessage = "{0}_{1}" -f $date,$Message
  $logMessage | Out-File -FilePath $logFilePath -Append
}

function logPrepend($Message) {
  $logFilePath = 'C:\temp\myLogP.txt'

  $date = Get-Date -Format 'yyyyMMddHHmmss'
  $logMessage = "{0}_{1}" -f $date,$Message
  if(Test-Path -Path $logFilePath) {
    $logFileContent = Get-Content -Path $logFilePath
  } else {
    $logFileContent = ''
  }
  $logMessage,$logFileContent | Out-File -FilePath $logFilePath 
}

$processes = Get-Process

$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
foreach ($p in $processes)
{
  logAppend($p.ProcessName)
}

$stopwatch.Stop()
$stopwatch.Elapsed


$stopwatch =  [system.diagnostics.stopwatch]::StartNew()
foreach ($p in $processes)
{
  logPrepend($p.ProcessName)
}

$stopwatch.Stop()
$stopwatch.Elapsed
我已经运行了好几次,直到日志文件中有几千行。 从1603到1925行,我的结果是:

附加:7.0167008 s
Prepend:21.7046793 s

尽管如前所述,我很难相信此函数会占用您的内存,但您可以对其进行优化:

function log ([string]$Message) {
    $logFilePath = 'C:\logPath\myLog.txt'
    # prefix the message with the current date
    $Message = "{0:yyyyMMddHHmmss}_{1}" -f (Get-Date), $Message
    if (Test-Path -Path $logFilePath -PathType Leaf) {
        # newest log entry on top: append current content
        $Message = "{0}`r`n{1}" -f $Message, (Get-Content -Path $logFilePath -Raw)
    }

    Set-Content -Path $logFilePath -Value $Message
}

Powershell或.Net有一个垃圾收集器,因此释放内存不是即时的。此外,Powershell 7中的内存管理可能更好。我多次尝试重复您的函数,但内存使用率没有超过几百兆

可能有一种更有效的.net方法可以将一行前置到文件中:

我有一种奇怪的方式来写一行字。我不确定这对一个大文件有多好。对于700兆的文件,工作集内存保持在76兆

$c:file
one
two
three

$c:file = 'pre',$c:file

$c:file
pre
one
two
three

ps powershell

Handles NPM(K) PM(K) WS(K) CPU(s)   Id SI ProcessName
------- ------ ----- ----- ------   -- -- -----------
    607     27 67448 76824   1.14 3652  3 powershell


函数将消息前置到日志文件(将其置于顶部),而append将其置于末尾)。日志文件为300 KB。我将尝试这个方法,但它没有回答我的问题“为什么函数中的变量没有自动销毁”。我已经更新了答案,根据js2010指出的答案,包含了一种更有效的方法来预处理文件。您是如何发现这个函数会占用所有RAM的?在我看来,这不仅仅是一个令人眼花缭乱的问题,我认为这个问题是由你的剧本中的其他地方引起的。您是否正在创建COM对象而不从内存中释放它们?或者使用.NET对象或Windows窗体而不使用.Dispose()销毁它们?虽然函数在顶部写新行,但它会消耗额外的内存,因为它必须读取文件的当前内容,然后通过将其重新创建到新数组中再次使用额外的内存,但是对于300kb的文件,这应该不会有任何问题。正如
Theo
指出的,您正在读取的文件越来越大。放弃你对内容预挂起的奇怪要求,切换到append,你的日志功能将停止使用不断增长的RAM。谢谢!我试图找到有关垃圾收集的更多信息,并找到了这篇有用的文章,其中的内容是
[System.GC]::Collect()
启动垃圾收集,但不会等到垃圾收集完成,而
[System.GC]::getTotalMemy($true)
将启动并等待垃圾收集