मेरे पास निम्न सरल कार्य है जो एक स्क्रिप्ट में कई बार उपयोग किया जाता है जो एक निर्देशिका के माध्यम से पुनरावृत्त होता है और इसमें फाइलों की उम्र की जांच करता है।

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-Variable logMessage,logFileContent,logFilePath,date जोड़कर रैम समस्या को ठीक किया, लेकिन यह जानना चाहूंगा कि इस राम समस्या को अन्यथा कैसे हल किया जा सकता है और फ़ंक्शन के भीतर चर स्वचालित रूप से नष्ट क्यों नहीं होते हैं।

3
Guenther Schmitz 8 मई 2020, 08:57
1
आपने कैसे पता लगाया कि यह फ़ंक्शन सभी RAM को खा जाता है? आईएमओ आंखों के अंत से मिलने के अलावा और भी है, मुझे लगता है कि समस्या आपकी लिपि में कहीं और होती है। क्या आप शायद COM ऑब्जेक्ट्स को स्मृति से मुक्त किए बिना बना रहे हैं? या .NET ऑब्जेक्ट्स या विंडोज फॉर्म को नष्ट किए बिना .Dispose() से उपयोग करें? यद्यपि फ़ंक्शन शीर्ष पर नई लाइनें लिखता है, यह अतिरिक्त मेमोरी का उपयोग करेगा क्योंकि इसे फ़ाइल की वर्तमान सामग्री में पढ़ना है, और फिर इसे एक नई सरणी में फिर से बनाकर अतिरिक्त मेमोरी का उपयोग किया जाता है, लेकिन 300kb फ़ाइल के साथ यह होना चाहिए कोई समस्या न हो।
 – 
Theo
8 मई 2020, 11:59
1
जैसा कि Theo ने बताया, आप एक ऐसी फ़ाइल में पढ़ रहे हैं जो लगातार बड़ी होती जाती है। सामग्री को प्री-पेंड करने के लिए अपनी अजीब आवश्यकता को छोड़ दें, संलग्न करें पर स्विच करें, और आपका लॉग फ़ंक्शन रैम की लगातार बढ़ती मात्रा का उपयोग करना बंद कर देगा।
 – 
Lee_Dailey
8 मई 2020, 16:42

3 जवाब

सबसे बढ़िया उत्तर

Powershell या .Net में कचरा संग्रहकर्ता है, इसलिए स्मृति को मुक्त करना तत्काल नहीं है। पावरशेल में स्पीड स्क्रिप्ट के लिए कचरा संग्रह साथ ही स्मृति प्रबंधन शायद बेहतर है पॉवरशेल 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

3
js2010 9 मई 2020, 23:04
धन्यवाद! मैंने कचरा संग्रहण के बारे में अधिक जानकारी खोजने की कोशिश की और यह उपयोगी लेख पाया jhouseconsulting.com/2017/09/25/… जहां यह लिखा है कि [System.GC]::Collect() कचरा संग्रहण शुरू करता है, लेकिन उसके समाप्त होने तक इंतजार नहीं करेगा जबकि [System.GC]::GetTotalMemory($true) आरंभ करेगा और कचरा संग्रहण की प्रतीक्षा करें
 – 
Guenther Schmitz
9 मई 2020, 20:22

हालांकि जैसा कि टिप्पणी की गई है, मैं शायद ही विश्वास कर सकता हूं कि यह फ़ंक्शन आपकी याददाश्त को बढ़ाता है, आप इसे अनुकूलित कर सकते हैं:

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
}
0
Theo 8 मई 2020, 16:38

मैं सिर्फ इस बात से इंकार करना चाहता हूं कि फ़ाइल में प्रीपेन्ड करने के कारण रैम का उपयोग होता है। क्या आपने लॉग सामग्री को एक चर में संग्रहीत नहीं करने का प्रयास किया है? अर्थात।

$logMessage,(Get-Content -Path $logFilePath) | Out-File -FilePath $logFilePath

5/8/20 संपादित करें - पता चलता है कि प्रीपेन्डिंग (जब कुशलतापूर्वक किया जाता है) उतना धीमा नहीं है जितना मैंने सोचा था - यह उसी क्रम में है जैसे -Append का उपयोग करना। हालांकि कोड (वह js2010) इंगित किया गया) लंबा और बदसूरत है, लेकिन अगर आपको वास्तव में फ़ाइल को प्रीपेन्ड करने की ज़रूरत है, तो इसे करने का यह तरीका है।

मैंने ओपी के कोड को स्वचालित रूप से एक नई लाइन डालने के लिए थोड़ा सा संशोधित किया।

function log-prepend{
  param(
    $content,
    $filePath = 'C:\temp\myLogP.txt'
  )
    $file = get-item $filePath
    if(!$file.exists){
      write-error "$file does not exist";
      return;
    }
    $filepath = $file.fullname;
    $tmptoken = (get-location).path + "\_tmpfile" + $file.name;
    write-verbose "$tmptoken created to as buffer";
    $tfs = [System.io.file]::create($tmptoken);
    $fs = [System.IO.File]::Open($file.fullname,[System.IO.FileMode]::Open,[System.IO.FileAccess]::ReadWrite);
    try{
      $date = Get-Date -Format 'yyyyMMddHHmmss'
      $logMessage = "{0}_{1}`r`n" -f $date,$content
      $msg = $logMessage.tochararray();
      $tfs.write($msg,0,$msg.length);
      $fs.position = 0;
      $fs.copyTo($tfs);
    }
    catch{
      write-verbose $_.Exception.Message;
    }
    finally{

      $tfs.close();
      $fs.close();
      if($error.count -eq 0){
        write-verbose ("updating $filepath");
        [System.io.File]::Delete($filepath);
        [System.io.file]::Move($tmptoken,$filepath);
      }
      else{
        $error.clear();
        [System.io.file]::Delete($tmptoken);
      }
    }
}

यहाँ मेरा मूल उत्तर था जो दिखाता है कि स्टॉपवॉच का उपयोग करके समय का परीक्षण कैसे किया जाता है।

जब आप किसी लॉग फ़ाइल को प्रीपेन्ड करते हैं, तो आप पूरी लॉग फ़ाइल को मेमोरी में पढ़ रहे होते हैं, फिर उसे वापस लिख रहे होते हैं।

आपको वास्तव में एपेंड का उपयोग करना चाहिए - जिससे स्क्रिप्ट बहुत तेज चलेगी।

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

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

संपादित करें: आपको यह समझाने के लिए कि लॉग फ़ाइल के लिए तैयार करना एक बुरा विचार है, यहाँ एक परीक्षण है जो आप अपने सिस्टम पर कर सकते हैं:

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 प्रीपेन्ड: 21.7046793 s

0
Ambrose Leung 8 मई 2020, 21:14
1
फ़ंक्शन संदेश को लॉगफाइल में जोड़ता है (इसे शीर्ष पर रखता है) जबकि परिशिष्ट इसे अंत में रखेगा)।
 – 
Guenther Schmitz
8 मई 2020, 10:27
लॉग फ़ाइल 300 केबी है। मैं यह कोशिश करूँगा लेकिन यह मेरे प्रश्न का उत्तर नहीं देता है "फ़ंक्शन में चर स्वचालित रूप से नष्ट क्यों नहीं होते हैं"।
 – 
Guenther Schmitz
8 मई 2020, 10:51
मैंने एक उत्तर के आधार पर फ़ाइल को प्रीपेन्ड करने के लिए एक और अधिक कुशल तरीका शामिल करने के लिए उत्तर अपडेट किया है जो कि js2010 ने इंगित किया है।
 – 
Ambrose Leung
8 मई 2020, 21:15