2012-06-14 10 views
8

Si tengo una referencia e para una parte de una colección persistente, ¿se puede recolectar toda la colección? ¿Lo entiendo correctamente?¿Se recogen las colecciones persistentes de basura?

La función gctest es solo para probar el comportamiento de las colecciones.

(defn gctest 
    "A shot about testing the gc-ability of persistent thingies." 
    [n] 
    (take 5 (drop 100 (vec (range n))))) 

main=> (def a (gctest 1e7)) 
main=> (def b (gctest 1e7)) 
main=> (def c (gctest 1e7)) 
main=> (def d (gctest 1e7)) 
main=> (def e (gctest 1e7)) 
main=> (def f (gctest 1e7)) 
main=> (def g (gctest 1e7)) 

OutOfMemoryError GC overhead limit exceeded clojure.lang.ArrayChunk.dropFirst  (ArrayChunk.java:54) 

Ya oí hablar de la retención de espuma, pero esto parece un poco más general o no?

Lo que quiero entender es cómo puedo usar una gran colección de cambios. Espero que grandes partes de las colecciones cambien con el tiempo, de manera que las piezas grandes pueden ser recogidas de basura en principio, pero no todas.

¿Hay una forma estándar de hacer frente a esto?

Respuesta

7

Norma GC estándar: mientras mantenga la referencia a una parte de la colección, todos los objetos accesibles desde sus referencias permanecen en la memoria. Entonces solo la parte de la colección que es accesible de sus referencias será mantenga, el resto será recolectado. En particular, si se refiere a los últimos 50 elementos de la lista de 100 elementos, los primeros 50 elementos se recopilarán y el resto permanecerá en la memoria.

Sin embargo, en su caso se conservarán todos los elementos de cada colección a partir de la centésima. Y el motivo es evaluación diferida. La función take produce lazy secuencia de (en su caso) 5 elementos. El objeto de secuencia diferida en sí mismo no es una secuencia real, sino que es un objeto generador especial (aunque no es el término de Clojure, sino más bien el de Python). Cuando necesite elemento de secuencia diferida, el objeto generador lo genera y lo devuelve. Pero si no solicita el elemento, el generador simplemente mantiene referencias a todos los objetos que pueda necesitar para generar un elemento.

En su ejemplo se crean grandes vectores y piden 5 elementos de ella, y luego guardar el resultado a las variables a, b, c, etc. Clojure hace grande del vector y generador objeto, señalando elemento 100a. La referencia a la colección en sí se pierde, pero la referencia al objeto del generador se guarda en el nivel superior. Nunca se evalúan los objetos del generador y, por lo tanto, nunca se hacen secuencias de 5 elementos reales. REPL se refiere a los vars a, b, c, etc., estos valores se refieren a objetos generadores, y los objetos generadores se refieren a las colecciones que necesitan para producir secuencias reales de 5 elementos. Por lo tanto, todos los elementos (excepto los primeros 100 de ellos) de todas las colecciones deben permanecer en la memoria.

Por otro lado, si usted evalúa objetos generadores, producirán secuencias de 5 elementos reales y olvidarán la referencia al resto de la colección. Prueba esto:

user> (def a (gctest 1e7)) 
#'user/a                                    
user> (println a) 
(100 101 102 103 104)                                 
nil                                      
user> (def b (gctest 1e7)) 
#'user/b                                    
user> (println b) 
(100 101 102 103 104)                                 
nil                                      
user> (def c (gctest 1e7)) 
#'user/c                                    
user> (println c) 
(100 101 102 103 104)                                 
nil                                      
user> (def d (gctest 1e7)) 
#'user/d                                    
user> (println d) 
(100 101 102 103 104)                                 
nil                                      
user> (def e (gctest 1e7)) 
#'user/e                                    
user> (println e) 
(100 101 102 103 104)                                 
nil                                      
user> (def f (gctest 1e7)) 
#'user/f                                    
user> (println f) 
(100 101 102 103 104)                                 
nil                                      
user> (def g (gctest 1e7)) 
#'user/g                                    
user> (println g) 
(100 101 102 103 104)                                 
nil                                      
user> (def h (gctest 1e7)) 
#'user/h                                    
user> (println h) 
(100 101 102 103 104)                                 
nil                                      
user> (def i (gctest 1e7)) 
#'user/i                                    
user> (println i) 
(100 101 102 103 104)                                 
nil 

No hay OutOfMemory! Vars a, b, c, etc. ahora almacenan listas reales de 5 elementos y, por lo tanto, no hay más referencias a colecciones grandes, por lo que se pueden recopilar.

+1

Así que una vez fui demasiado flojo. Estupendo. – Falko

4

Este tipo de problemas se pueden resolver usando secuencias perezosas. En su caso, ha utilizado la función vec que realmente crea un vector en la memoria yendo a través de cada elemento generado por la función range (el rango devuelve una secuencia perezosa).

A continuación código (sin vec llamada no tendrá el problema de memoria)

(defn gctest 
    "A shot about testing the gc-ability of persistent thingies." 
    [n] 
    (take 5 (drop 100 (range n)))) 

ACTUALIZACIÓN:

Cuando se utiliza la llamada vec, se conservarán todos los elementos de la memoria y no recogidos por GC porque estos elementos son referenciados (y requeridos) por el objeto de secuencia devuelto por la función gctest para que pueda obtener los elementos necesarios (es decir, omitiendo 100 elementos y tomando 5 elementos) cuando se solicita el objeto de secuencia ed para elementos.

+0

Lo sentimos, la función era solo como demostración. Edité la pregunta con la esperanza de ser más comprensible. – Falko

+1

Ankur - Creo que usar vec fue el punto de la pregunta: ¿qué elementos de ese vector se mantendrían? – Hendekagon

+0

Todos los elementos del vector se conservarán porque es requerido por el objeto seqeuence lazy devuelto por 'gctest' – Ankur

Cuestiones relacionadas