Digamos que es un recolector de basura de marcado y barrido concurrente.
Cuando dicho GC maneja punteros constantes, simplemente los recorre (comenzando desde las raíces) y marca cada bloque de datos encontrado. Luego barre todo sin marcar. Un código de cliente debe marcar los bloques de datos que usa como raíces.
¿Pero qué hacer con las variables? Aquí hay una situación:
V
es una variable que almacena un puntero al objetoA
.Thread 1
leeV
y suspende.Thread 2
modificaV
y hace que apunte al objetoB
.- El recolector de basura ejecuta su fase de "marca" y encuentra que
A
ya no se hace referencia, luego lo desasigna durante la fase de "barrido". Thread 1
despierta e intenta usarA
(ya leyóV
en el paso 2) marcándolo como root. Y falla , porqueA
ya no existe.
Entonces, ¿cómo manejar esto?
El Thread 2
puede marcar el objeto reemplazado A
con una bandera especial do-no-remove (bandera similar se utiliza para objetos recién asignados). ¿Pero cuándo se debe eliminar esta bandera? Por supuesto que Thread 1
podría hacer eso. Pero Thread 2
no sabe nada Thread 1
y, por lo tanto, no puedo estar seguro de que esto se haga nunca. Esto puede llevar a A
que nunca se libere. Y si GC eliminará esa bandera, entonces nada impide que A
se elimine cuando GC se ejecute por segunda vez ...
Las descripciones del recolector de basura de marca y barrido sobre la marcha que he leído solo mencionan que el objeto reemplazado debe estar "atenuado". Pero sin ningún detalle. Un enlace a una descripción más detallada de la solución sería muy apreciado.
fuente
Debe marcar las variables locales en algún momento durante la fase de marca. Todas las variables locales, incluidas las que normalmente viven en la pila. De algun modo.
Además, creo que debe hacerse durante la fase síncrona (todos los mutantes detenidos) de escanear objetos modificados. De hecho, el mismo problema podría surgir incluso sin considerar las variables / registros locales. Considere el objeto A apuntando a nulo y el objeto B apuntando a C. Ahora escanea el objeto A, aparece un hilo mutador, copia la referencia a C de B a A, anula B. Y ahora puede escanear B. Y C se deslizó debajo de tus dedos
No conozco ninguna forma de lidiar con esto que no implique detener a los mutadores. La técnica habitual es al final de la fase de marcado para detener a todos los mutadores y volver a marcar todos los objetos que mutaron durante la fase de marcado principal. E incluya pilas y registros en eso.
Los registros de marcado normalmente se solucionan haciéndolo sincrónicamente llamando al recopilador en el hilo a veces. Dentro de la función de recopilador, solo sus propias variables locales (que no son raíces) pueden estar en registros, todas las demás variables locales en la cadena de llamadas están en la pila, por lo que puede caminar por la pila.
Alternativamente, puede enviar una señal al hilo. El controlador de señal volverá a forzar todas las variables en la pila, para que pueda recorrerlas. La desventaja de este método es que depende de la plataforma.
fuente