2010-01-05 9 views
5

Estoy trabajando en mi camino a través del código de Ferret (puerto de Rubén de Lucene) para resolver un error. El código del hurón es principalmente una extensión C para Ruby. Me encuentro con algunos problemas con el recolector de basura. Me las arreglé para arreglarlo, pero I no entiendo completamente mi solución =) Espero que alguien con más conocimiento de la extensión Ruby y C (este es mi tercer día con Ruby) puede elaborar. Gracias.Recolección de basura con Extensión de Ruby C

Aquí está la situación:

Algunos donde en código C Ferret, estoy volviendo un "token" a Ruby tierra. El código es el siguiente

static VALUE get_token (...) 
{ 
    ... 
    RToken *token = ALLOC(RToken); 
    token->text = rb_str_new2("some text"); 
    return Data_Wrap_Struct(..., &frt_token_mark, &frt_token_free, token); 
} 

frt_token_mark llama rb_gc_mark (tokens> texto) y frt_token_free simplemente libera el token con conexión (token)

En Ruby, este código se corresponde con lo siguiente:

token = @ input.next

Básicamente, @input se establece en algún objeto, llamando al siguiente método desencadena la llamada a get_token C, que devuelve un objeto token.

En Rubí tierra, que luego hacer algo como w = token.text.scan ('\ w +')

Cuando ejecuto el código dentro de un bucle while 1 (para aislar mi problema), en algunos punto (más o menos cuando mi proceso de rubí mem huella va a 256MB, probablemente cierto umbral GC), ruby ​​muere con errores como

método de exploración llamados en el objeto terminado

O sólo volcado de memoria. Yo creo que token.text fue basura recolectada.

No sé lo suficiente sobre la extensión Ruby C para saber qué ocurre con Data_Wrap_Struct objetos devueltos. Me parece que la asignación en Ruby land, token =, debería crear una referencia a ella.

Mi "solución temporal"/"arreglar" es crear una variable de instancia en el objeto de Ruby mencionado por @input, y almacena el texto token en allí, a obtener una referencia adicional a ella. Así que el código C se parece

RToken *token = ALLOC(RToken); 
token->text = rb_str_new2(tk->text); 
/* added code: prevent garbage collection */ 
rb_ivar_set(input, id_curtoken, token->text); 
return Data_Wrap_Struct(cToken, &frt_token_mark, &frt_token_free, token); 

Así que ahora he creado un "curtoken" en la variable de instancia de entrada y guarda una copia del texto que hay ... Me he encargado de eliminar/borrar esta referencia en la devolución de llamada gratuita de la clase para @input.

Con este código, funciona porque ya no recibo el objeto terminado error.

La solución parece tener sentido para mí - que mantiene una referencia adicional en curtoken a la cadena token.text por lo que no se puede quitar una instancia de token.text hasta la próxima vez @ input.next es llamado (en cuyo momento un token.text diferente reemplaza el antiguo valor en curtoken).

Mi pregunta es: ¿por qué no funcionó antes? ¿No debería Data_Wrap_Structure devolver un objeto que, cuando se le asigna en Ruby land, tiene una referencia válida y no debe ser eliminado por Ruby?

Gracias.

+0

Por cierto, supongo que en C, cuando devuelva Data_Wrap_Struct, realmente debería crear una variable de VALOR, asignarle el resultado de Data_Wrap_Struct, y mantener una referencia de esa variable de VALOR en algún lugar, y esa debería ser la convención normal de devolver un VALOR - necesita mantener una referencia manualmente. Creo que es una solución mejor que lo que he mostrado antes. Pero por favor comenta Gracias. – OverClocked

Respuesta

3

Cuando se invoca el recolector de basura Ruby, tiene una fase de marca y una fase de barrido. La marca marcas de fase todos los objetos en el sistema de marcado:

  1. todos los objetos referenciados por un marco de pila rubí (por ejemplo, las variables locales)
  2. todo globalmente objetos accesibles (por ejemplo, se hace referencia por una variable constante o global) y sus hijos/referentes, y
  3. todos los objetos a los que hace referencia una referencia en la pila, así como los hijos/referentes de esos objetos.

, así como una serie de otros objetos que no son importantes para esta discusión. La fase de barrido destruye cualquier objeto que no sea accesible (es decir, aquellos que no fueron marcados).

Data_Wrap_Struct devuelve una referencia a un objeto. Siempre que esa referencia esté disponible para el código ruby ​​(por ejemplo, almacenada en una variable local) o esté en la pila (a la que se hace referencia mediante una variable C local), el objeto no debe barrirse.

Parece que por lo que has publicado ese token-> text está siendo recogido. Pero, ¿por qué se está recogiendo? No debe ser marcado. ¿El objeto Token se está marcando? Si lo es, entonces token-> text debería estar marcado. Intente establecer un punto de interrupción o imprimir un mensaje en la función de marca del token para ver.

Si no se marca el token, el siguiente paso es averiguar por qué. Si se está marcando, entonces el siguiente paso es descubrir por qué la cadena devuelta por el método text() está siendo barrida (tal vez no sea el mismo objeto que se está marcando).

Además, ¿está seguro de que es el miembro de texto del token el que está causando la excepción? En cuanto a:

http://github.com/dbalmain/ferret/blob/master/ruby/ext/r_analysis.c

veo que el token y la cadena de componentes léxicos tienen tanto texto) (métodos. La estructura TokenStream no contiene una referencia a su objeto de texto (no puede, ya que es una estructura C sin conocimiento de ruby). Por lo tanto, el objeto Ruby que envuelve la estructura C necesita contener la referencia (y esto se hace con rb_ivar_set).

La estructura RToken no debería necesitar hacer esto, ya que marca su miembro de texto en su función de marca.

Una cosa más: es posible que pueda reproducir este error llamando a GC.start explícitamente en su bucle en lugar de tener que asignar tantos objetos que el recolector de basura entre. Esto no solucionará el problema pero podría ocasionar diagnóstico más simple.

+1

Paul: La pregunta es qué es lo "correcto" al devolver un VALOR de un procedimiento en C a la tierra de Ruby. Ferret tenía: return Data_Wrap_Struct ... en este caso, lo que devuelve Data_Wrap_Struct es 1) no en C stack, ya que se devuelve desde el procedimiento C, y 2) no en un objeto Ruby. Mi solución fue no "volver Data_Wrap_Struct", pero VALOR v = Data_Wrap_struct ... rb_ivar_set (.., &v); retorno v; que la fija Quiero confirmar con alguien:. Data_Wrap_Struct no puede ser devuelto directamente de C, pero necesita ser referenciado en un obj de Ruby para evitar que se coseche. Gracias. – OverClocked

Cuestiones relacionadas