Verwerken XML-bestand geeft itgenclr007 - Application is using more memory than available

Out of Memory bij verwerken grote bestanden

Bij het uitvoeren van een SQL-statement kan een OutOfMemoryException optreden:

itgenclr007:
Application is using more memory than available.
Restarting the application, use 64-bit Windows and Office or making more memory available might resolve this problem.
Exception of type ‘System.OutOfMemoryException’ was thrown.
ccurred (UTC): 5/16/2022 6:23:04 AM

Alle Invantive SQL programmatuur is geschikt om in 64-bit modus te draaien en geeft daar ook de voorkeur aan. Het beschikbare werkgeheugen is daardoor meestal vele gigabytes en die worden ook automatisch gebruikt bij omvangrijke en massaal parallelle verwerking zoals op Invantive Cloud. De enige voorkomende uitzondering is bij gebruik binnen de 32-bit versie Microsoft Office; hierbij is het beschikbare geheugen meestal beperkt tot circa 1,5 GB.

Echter, de System.OutOfMemoryException kan ook optreden als er ruim voldoende werkgeheugen beschikbaar is en een 64-bit versie uitgevoerd wordt.

Een individuele tekst in de Microsoft CLR voor .net heeft een maximale omvang van circa 2 GB (2^31) bytes. Uitgaande van 2 bytes per karakter is de limiet circa 1 miljard karakters. In de praktijk loopt men hier wel eens tegenaan bij het verwerken van omvangrijke JSON of XML-bestanden.

Een voorbeeld hiervan is het volgende Invantive SQL-statement dat alle bestanden met de extensie ‘trx’ (Visual Studio Test Results File) uitleest en omzet naar een tabelstructuur:

select xml.class_name
,      xml.test_id
from   files@os('c:\temp\vstest-output', '*.trx', false) fle
join   xmltable
       ( '/*["TestRun"=local-name()]'
         || '/*["TestDefinitions"=local-name()]' 
         || '/*["UnitTest"=local-name()]'
         || '/*["TestMethod"=local-name()]'
         passing file fle.file_path
         columns class_name                    varchar2 not null path '@className'
         ,       test_id                       guid     not null path '../@id'
       ) xml

Dit kan ook geschreven zijn met een read_file_text als:

select xml.class_name
,      xml.test_id
from   versionelements@inmemorystorage vet
join   files@os('${TEST_RESULTS_FOLDER}', '*.trx', false) fle
join   tests@inmemorystorage tst
on     fle.file_path like '%' || basename(tst.test_dll, '.dll') || '.trx'
join   read_file_text@Os(fle.file_path) rfe
join   xmltable
       ( '/*["TestRun"=local-name()]/*["TestDefinitions"=local-name()]/*["UnitTest"=local-name()]/*["TestMethod"=local-name()]'
         passing rfe.file_contents
         columns class_name                    varchar2 not null path '@className'
         ,       test_id                       guid     not null path '../@id'
       ) xml

Al bij een TRX-bestand van ruim 1 gigabyte zal een OutOfMemoryException optreden, omdat het TRX-bestand als UTF8 is vastgelegd waarbij de veelvoorkomende karakters uit de ASCII-reeks maar 1 byte opslag vragen. Bij het uitlezen wordt dit automatisch omgezet in UTF-16, waardoor het geheugenbeslag voor de tekst al boven de 2 GB uitkomt.

Dit is terug te lezen in de call stack:

   at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
   at System.Text.UTF8Encoding.GetString(Byte[] bytes, Int32 index, Int32 count)
   at lambda_method(Closure , GlobalState , ExecutionOptions , IQueryStatePerExecution , ISparseArray , ISparseArray )
   at Invantive.Sql.V1.FirehoseResultSet.JB.MoveNext()
   at Invantive.Sql.V1.FirehoseResultSet.HK.MoveNext()
   at Invantive.Data.CompressedEnumerable`1.<GetEnumerator>d__10.MoveNext()
   at Invantive.Sql.V1.FirehoseResultSet.JB.MoveNext()
...

De Microsoft-interne aanroep van UTF8Encoding verzorgt deze omzetting.

Hoe los ik een itgenclr007 op?

De meest ideale oplossing is om eerst te upgraden naar versie 22.1.56 of nieuwer en dan nogmaals te proberen. Vanaf deze release zijn er faciliteiten toegevoegd om extreem grote tekstbestanden met data te verwerken.

De alternatieve aanpak is om de bestanden op te hakken in delen voor verwerking in Invantive SQL. Dit kan soms door het programma dat ze aanmaakt opdracht te geven per 100 megabyte een nieuw bestand te maken of door kleinere input aan te bieden. Als dat niet mogelijk is, dan is een externe hulpprogramma nodig dat het grote bestand uit elkaar haalt en als losse bestanden toevoegt. Soms kan een extern hulpprogramma gerealiseerd worden met commandoregel tools, maar soms kan dit niet doordat relevante tools ontbreken of de tools zelf geen bestanden groter dan 2 GB kunnen verwerken. Het laatste redmiddel is een programma te schrijven in een programmeertaal.