Powershell, создающий до 50 000 XML-файлов, приводит к утечке памяти

Мне нужно просмотреть файл csv, содержащий до 58 тыс. строк. Каждая строка имеет 60 значений, разделенных точкой с запятой. Для каждой строки мне нужно создать один XML-файл. Мой сценарий делает то, что я ожидал, но на создание файлов размером 39 КБ уходит 20 часов, и он работает очень медленно, поскольку потребляет всю системную память. Всего у вас есть 10 файлов csv, содержащих от 30 до 90 тысяч строк. Для каждой из этих строк мне нужно создать один XML-файл.

Как я могу избежать этого потребления памяти, есть ли лучший способ сделать это?

Я читаю данные csv с помощью Import-csv, зацикливаясь на каждом идентификаторе в файле. Это мой код:

функция createxmlfromcsvcharge{

$csv = Import-csv $conf_YearFiles$conf_File -Delimiter ";" 
    
foreach($ChargingID in $CSV){

    $ID = $ChargingID.ChargingID
    
    #file name 
    $FileName = "MIS_Record_2013_"+$ID+".xml"
    
    # Creating a simple XML document
    $xmlDocument = New-Object System.Xml.XmlDocument
    
    $dec = ($xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", $null))
    $root = $xmlDocument.DocumentElement
    $xmlDocument.InsertBefore($dec,$root)
            
    # Adding a root element
    $rootElement = $xmlDocument.CreateElement("ChargingRecord")
    $xmlDocument.AppendChild($rootElement)
    
    # create <ChargingID> container node
    $xmluser = $xmlDocument.CreateElement('Charge')
    $rootElement.AppendChild($xmluser)
    
    # add each property from csv entry to <ChargingID>
    Foreach ($property in $csv[0].psobject.Properties.name) {
        # create a property node
        $xmlproperty = $xmlDocument.CreateElement($property)
        $text = $xmlDocument.CreateTextNode($ChargingID.$property)      
        $xmlproperty.AppendChild($text)
        # add to current <ChargingID> node
        $xmluser.AppendChild($xmlproperty)
    }
    
    $xmlDocument.Save('m:\temp\2013\'+$FileName)
}

}

Код генерирует именно то, что я ожидаю.

Любая помощь или подсказки действительно ценятся. Спасибо.

Измените $xmlDocument.InsertBefore(...) на [void]$xmlDocument.InsertBefore(...), затем сделайте то же самое со всеми вызовами AppendChild(...) - в противном случае эти методы выведут ссылку на вставленный/добавленный узел, заставляя PowerShell собирать ее как выходные данные, что, в свою очередь, предотвращает сбор мусора экземпляров XML-документа. из предыдущих итераций

Mathias R. Jessen 10.05.2024 14:35

Привет, Матиас! Большое спасибо за подсказку, все работает отлично! !

Ivo 10.05.2024 16:09

Рад слышать! Я добавил правильный ответ ниже :)

Mathias R. Jessen 10.05.2024 16:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это требует много времени и памяти, поскольку управляет полным весом файла и относительного объекта.

Простое подключение Import-Csv к Foreach-Object должно улучшить ситуацию:

$DestinationDirectory = 'm:\temp\2013\' 

Import-Csv $conf_YearFiles$conf_File -Delimiter ';' |
    ForEach-Object {
        $ID = $PSItem.ChargingID
        
        #file name 
        $FileName = Join-Path -Path $DestinationDirectory -ChildPath "MIS_Record_2013_${ID}.xml"
        
        # Creating a simple XML document
        $xmlDocument = New-Object System.Xml.XmlDocument
        
        $dec = ($xmlDocument.CreateXmlDeclaration('1.0', 'UTF-8', $null))
        $root = $xmlDocument.DocumentElement
        $xmlDocument.InsertBefore($dec, $root)
                
        # Adding a root element
        $rootElement = $xmlDocument.CreateElement('ChargingRecord')
        $xmlDocument.AppendChild($rootElement)
        
        # create <ChargingID> container node
        $xmluser = $xmlDocument.CreateElement('Charge')
        $rootElement.AppendChild($xmluser)
        
        # add each property from csv entry to <ChargingID>
        Foreach ($property in $PSItem.psobject.Properties.name) {
            # create a property node
            $xmlproperty = $xmlDocument.CreateElement($property)
            $text = $xmlDocument.CreateTextNode($PSItem.$property)      
            $xmlproperty.AppendChild($text)
            # add to current <ChargingID> node
            $xmluser.AppendChild($xmlproperty)
        }
        
        $xmlDocument.Save( $FileName)
    }
Ответ принят как подходящий

Как намекают в комментариях, нехватка памяти вызвана поведением XmlDocument API!

Когда вы вызываете InsertNode(...) или AppendChild(...), возвращается вставленный/добавленный экземпляр узла.

Если вы не отбросите это возвращаемое значение активно, процессор среды выполнения PowerShell соберет его как выходные данные. Поскольку экземпляры узла ссылаются на документ-владелец, сборщик мусора не может очистить документы, созданные вами во время предыдущих итераций цикла, до тех пор, пока весь метод не вернется и вызывающая сторона не отбросит ссылки.

Решение состоит в том, чтобы отбросить возвращаемые значения на месте вызова — я предпочитаю приводить выражение вызова метода к [void], но вы можете передать его по каналу Out-Null или назначить $null для того же эффекта:

[void]$xmlDocument.InsertBefore($dec, $root)
# or
$null = $xmlDocument.InsertBefore($dec, $root)
# or 
$xmlDocument.InsertBefore($dec, $root) |Out-Null

Повторите эти действия для любых вызовов AppendChild() (и любого другого вызова метода, который возвращает ссылку на узел), и сборщик мусора сможет свободно выполнять свою работу :)

Другие вопросы по теме