¿Por qué gc () no libera memoria?

Ejecuto simulaciones en una computadora de 64 bits de Windows con 64 GB de RAM . El uso de la memoria alcanza el 55% y después de una ejecución de simulación finalizada elimino todos los objetos en el espacio de trabajo por rm(list=ls()) , seguido por un double gc() .

Supuse que esto liberaría suficiente memoria para la próxima ejecución de la simulación, pero en realidad el uso de memoria se reduce en solo un 1% . Consultando muchos foros diferentes no pude encontrar una explicación satisfactoria, solo comentarios vagos como:

“Dependiendo de su sistema operativo, la memoria liberada puede no devolverse al sistema operativo, pero mantenerse en el espacio de proceso”.

Me gustaría encontrar información sobre:

  • 1) qué sistema operativo y bajo qué condiciones la memoria liberada no se devuelve al sistema operativo, y
  • 2) si hay algún otro remedio que cerrar R y comenzar de nuevo para la siguiente ejecución de simulación?

¿Cómo se verifica el uso de la memoria? Normalmente, la máquina virtual asigna parte de la memoria que utiliza para almacenar sus datos. Algunos de los asignados pueden estar sin usar y marcados como gratuitos. Lo que GC hace es descubrir datos que no están referenciados desde ningún otro lugar y marcar los trozos de memoria correspondientes como no utilizados, esto no significa que esta memoria se libere en el sistema operativo. Sin embargo, desde la perspectiva de VM ahora hay más memoria libre que se puede utilizar para realizar más cálculos.

Como otros preguntaron, ¿experimentó errores de memoria? Si no, entonces no hay nada de qué preocuparse.

EDITAR: Esto y esto deberían ser suficientes para entender cómo funciona la asignación de memoria y la recolección de basura en R.

Desde el primer documento:

Ocasionalmente, se realiza un bash de liberar las páginas no utilizadas al sistema operativo. Cuando se lanzan las páginas, se retiene una cantidad de nodos libres igual a R_MaxKeepFrac por el número de nodos asignados para cada clase. Las páginas que no son necesarias para cumplir con este requisito son liberadas. Se realiza un bash de liberar páginas en cada colección R_PageReleaseFreq nivel 1 o nivel 2.

EDIT2:

Para ver la memoria utilizada, intente ejecutar gc () con verbose establecido en TRUE:

 gc(verbose=T) 

Aquí hay un resultado con una matriz de 10’000’000 enteros en la memoria:

 Garbage collection 9 = 1+0+8 (level 2) ... 10.7 Mbytes of cons cells used (49%) 40.6 Mbytes of vectors used (72%) used (Mb) gc trigger (Mb) max used (Mb) Ncells 198838 10.7 407500 21.8 350000 18.7 Vcells 5311050 40.6 7421749 56.7 5311504 40.6 

Y aquí está después de descartar la referencia a esto:

 Garbage collection 10 = 1+0+9 (level 2) ... 10.7 Mbytes of cons cells used (49%) 2.4 Mbytes of vectors used (5%) used (Mb) gc trigger (Mb) max used (Mb) Ncells 198821 10.7 407500 21.8 350000 18.7 Vcells 310987 2.4 5937399 45.3 5311504 40.6 

Como puede ver, la memoria utilizada por Vcells cayó de 40.6Mb a 2.4Mb.

El recolector de basura R es imperfecto de la siguiente manera (no tan) sutil: no mueve objetos (es decir, no compacta la memoria) debido a la forma en que interactúa con las bibliotecas C (Algunos otros lenguajes / implementaciones también sufren esto, pero otros , a pesar de tener que interactuar con C , logran tener un GC generacional de compactación que no sufre este problema).

Esto significa que si se turnan para asignar pequeños trozos de memoria que luego se descartan y trozos más grandes para objetos más permanentes (esta es una situación común cuando se procesa cadena / regexp), entonces la memoria se fragmenta y el recolector de basura no puede hacer nada al respecto it: la memoria se libera, pero no se puede reutilizar porque los trozos libres son demasiado cortos.

La única forma de solucionar el problema es guardar los objetos que desee, reiniciar R y volver a cargar los objetos.

Como está haciendo rm(list=ls()) , es decir, no necesita ningún objeto, no necesita guardar y volver a cargar nada, por lo tanto, en su caso, la solución es precisamente lo que desea evitar: reiniciar R .

PD. La recolección de basura es un tema altamente no trivial. Por ejemplo, Ruby utilizó 5 (!) Diferentes algoritmos GC durante 20 años . Java GC no apesta porque Sun / Oracle e IBM pasaron muchos años-hombre en sus respectivas implementaciones del GC. Por otro lado, R y Python tienen un pésimo GC, porque nadie se molestó en invertir los años-hombre necesarios, y son bastante populares. Eso es peor, es mejor para ti.