Tengo un archivo de texto MUY grande para analizar (~ 2GB). por varias razones, tengo que procesar el archivo en línea. Hago esto cargando el archivo de texto en la memoria (el servidor en el que estoy ejecutando el analizador tiene suficiente memoria) con var records = Regex.Split(File.ReadAllText(dumpPath, Encoding.Default), @"my regex here").Where(s => !string.IsNullOrEmpty(s));. esto consume RAM equivalente al tamaño del archivo de texto más algunos MB para la sobrecarga de IEnumerable. Hasta ahora, todo bien. luego repaso la colección con foreach (var recordsd in records) {...}

Aquí viene la parte interesante. Hago mucha manipulación de cadenas y expresiones regulares en el bucle foreach. luego, el programa rápidamente funciona con una System.OutOfMemoryException, aunque nunca uso más de unos pocos kB en el bucle foreach. Hice algunas instantáneas de memoria usando el generador de perfiles de mi elección (generador de perfiles de memoria ANTS), viendo millones y millones de objetos de cadena de Generación 2 en el montón, consumiendo toda la memoria disponible.

Viendo eso, yo - solo como una prueba - incluí una GC.Collect(); al final de cada iteración foreach, y listo, problema resuelto y no más excepciones de memoria (efectivamente debido a la recolecciones de basura permanentes, el programa ahora se ejecuta minuciosamente lento). La única memoria consumida es el tamaño del archivo real.

Ahora no puedo explicar por qué sucede esto y cómo prevenirlo. Según tengo entendido, en el mismo momento en que una variable sale del alcance y no tiene más referencias (activas) a ella, debe marcarse para la recolección de basura, ¿verdad?

En otra nota al margen, intenté ejecutar el programa en una máquina realmente enorme (64 GB de RAM). el programa finalizó correctamente, pero nunca liberó un solo byte de memoria antes de cerrarse. ¿Por qué? si no hay más referencias a un objeto y si el objeto sale del alcance, ¿por qué nunca se libera la memoria?

2
lightxx 18 abr. 2012 a las 13:14
1
¿No es la verdadera pregunta aquí por qué las cuerdas terminan en la generación 2? De alguna manera, debe aferrarse a ellos el tiempo suficiente para perderse las limpiezas gen0 y gen1. Una nota al margen. Podría intentar hacer GC.Collect(2, GCCollectionMode.Optimized) para acelerar la recopilación.
 – 
adrianm
18 abr. 2012 a las 14:39

1 respuesta

La mejor respuesta

ahora no puedo explicar por qué sucede esto y cómo prevenirlo. Según tengo entendido, en el mismo momento en que una variable sale del alcance y no tiene más referencias (activas) a ella, debe marcarse para la recolección de basura, ¿verdad?

No. No existe tal cosa como estar "marcado" para la recolección de basura, y las variables no son recolectadas de basura: los objetos sí lo son. Y un objeto que ya está en gen2 no será recolectado como basura hasta la próxima vez que el GC vea gen2, lo cual es relativamente raro.

por varias razones, tengo que procesar el archivo en línea.

Luego está tu respuesta: usa File.ReadLines si ' Estás usando .NET 4 y escribe el equivalente (es fácil) si no lo estás. Entonces no necesita todo el archivo en la memoria a la vez, solo una línea. El uso de su memoria debería caer en picado. (Tenga en cuenta que eso es ReadLines, no ReadAllLines; este último leerá todo el archivo en una matriz de cadenas, que no es lo que desea).

En otra nota al margen, intenté ejecutar el programa en una máquina realmente enorme (64 GB de RAM). el programa finalizó correctamente, pero nunca liberó un solo byte de memoria antes de cerrarse. ¿Por qué?

Si está hablando de la memoria que el proceso toma del sistema operativo, no creo que CLR alguna vez libere memoria. Supongo que adopta el enfoque de que si ha usado esa cantidad de memoria una vez, probablemente la usará nuevamente.

5
Community 20 jun. 2020 a las 12:12
Ok, probablemente escribí demasiado rápido y no volví a leer lo que realmente escribí. entonces, por varias otras razones, necesito el archivo de texto completo en la memoria porque también estoy haciendo mucho acceso aleatorio al archivo de texto. Además, solo quería una explicación de lo que estoy viendo. Gracias.
 – 
lightxx
18 abr. 2012 a las 13:20
Solo para elaborar un poco las cosas. el archivo es generado por un dispositivo de muestreo de ADN con un formato de archivo bastante críptico. algunas muestras de ADN se extienden más de una línea, otras no. la interpretación de los valores encontrados en una línea depende de los valores encontrados en otras líneas. y así. el objetivo es transformar ese enorme archivo de texto en una colección de objetos personalizados que se analizan y guardan en una base de datos. Es un poco complicado explicar todo eso, pero es importante ver el archivo como un todo mientras se analiza. todo lo que necesito saber es por qué llamar a GC.Collect (); parece resolver el problema y qué hacer al respecto
 – 
lightxx
18 abr. 2012 a las 13:38
Realmente quiero votar a favor tu comentario, pero mi reputación es demasiado baja :(
 – 
lightxx
18 abr. 2012 a las 13:46
@lightxx: Si bien rara vez es una buena idea llamarte GC.Collect() a ti mismo, puede haber algunas situaciones extrañas en las que sea útil, y parece que esta podría ser una de ellas.
 – 
Jon Skeet
18 abr. 2012 a las 13:51