Dispose en SPWeb, SPSite y SPListItemCollection, desarrollando Web Parts para SharePoint

Después de una extenuante semana de viaje viendo un caso fuera de Chile, el cual me obligó a estar offline casi todos los días, me doy un tiempo para escribir y dar a conocer los usuales problemas con que uno se enfrenta cuando analiza web parts que corren sobre SharePoint*.


Hasta hoy, he visto web parts desarrolladas que normalmente tienen pérdidas de memoria (memory leaks), principalmente de memoria no manejada pero también de memoria manejada.


Como consecuencia de lo anterior, el continuo uso de éstas (Web Parts mal codificadas), llevará irremediablemente a encontrarnos con [OOM] en nuestra aplicación. Una forma de sobrevivir ante este problema de pérdidas de memoria es configurar el pool de aplicación de IIS 6.0 o 7.0 para que recicle cuando la memoria consumida (privada o virtual) llegue a una cantidad de megabytes determinada. Para la versión 5.0, existe una aplicación llamada IIS Recycle que hace algo similar, aunque asp.net en IIS 5 tienes capacidades de reciclamiento.


El principal problema con el desarrollo de Web Parts para SharePoint es que, al menos hasta la versión 2003, SharePoint Portal depende mucho de componentes COM que utilizan memoria no manejada, y el desarrollador eso no lo sabe, no se da cuenta o definitivamente olvida liberar los recursos no manejados (que incluye llamar a Dispose cuando corresponda).


Si te interesa y quieres confirmar esto, dale una mirada con reflector al ensamblado Microsoft.SharePoint.Library.dll.


Adicionalmente al problema de la no liberación de recursos no manejados, algunas bibliotecas de SharePoint están programadas de tal forma de que si necesita un recurso y este no se ha instanciado (o se ha destruido adecuadamente), ésta lo vuelve a generar. Esto complica más el panorama ya que si un desarrollador fue cuidadoso y lo liberó, el posterior uso de otra función puede revivir el objeto. Esto no es cierto para todas las funciones, pero algunas tienen comportamiento peculiar. Veámoslo en acción (toda la acción que se puede tener en un blog [;)]).


Supongamos que un desarrollador obtiene una SPListItemCollection en un objeto oList y para liberar los recursos no manejados, llama responsablemente a oList.List.ParentWeb.Dispose(), pero si después decide obtener la cantidad de objetos de esta colección llamando a Count, se podrá vivir la situación detallada anteriormente. El llamado a Count revivirá el objeto SPWeb.


El método Dispose  de SPWeb llama a Close, que llama a el método a sobre at, que es de tipo Microsoft SharePoint.Library.a. Antes de seguir, ten en cuenta que ahora se enreda bastante más.

 




public void Dispose()


{


    this.Close();


}

 

public void Close()


{


    if (this.at != null)


    {


        this.at.a();


        this.at = null;


    }


}

 

Y luego, el llamado a Count de SPListItemCollection:






public override int get_Count()


{


    this.a(true);


    return this.c;


}

 

El código del método a (que está ofuscado) es:






private void a(bool A_0)


{


    string text;


    SPWeb web;

    a a; //Esto se ve mal, pero es producto de la ofuscación

    string viewXml;


    if (this.f)


    {

        web = this.b.Lists.Web;
       
a = web.l();
        <Cortado para abreviar…>t;

}

 

La variable web referencia ahora al valor de this.b.Lists.Web y luego se llama al método l (ele) (ofuscado también). Recordemos entonces que web referencia a un objeto SPWeb (Web) que está referenciado por un objeto SPListCollection (Lists) que no debe ser confundido con el objeto del cual llamamos a Count del tipo SPListItemCollection. Este último (SPListCollection) a su vez está referenciado por un objeto de tipo SPList (b), el cual es parte de nuestro objeto inicial, SPListItemCollection (¿no te dije que se enredaba?).


El método l (ele) es internal y el código que nos muestra reflector es el siguiente:






internal a l(){


    this.i();


    return this.at;


}

 

Como ven, está retornando la variable at. Esta misma variable era la que se había asignado a null cuando se ejecutó SPListItemCollection.List.ParentWeb.Dispose(). Conviene mostrar el código de ParentWeb disponible en SPList para aclarar toda la relación.


ParentWeb retorna el objeto SPWeb asociado a la variable m_Lists (del tipo SPListCollection).






public SPWeb ParentWeb


{


    get


    {


        return this.m_Lists.Web;


    }


}

 

Solo para demostrar lo anterior, el código de i y h se despliegan ahora. Verán que el objeto at es nuevamente generado si este es null.






private void i()


{


    if (this.at == null)


    {


        this.h();


    }


}


 


private void h()


{


    int num = this.b.i();


    bool flag = -1 == num;


    bool flag2 = null != this.b.g();

    this.at = g.a(!flag2, this.al, this.b.d(), ref num);  

    <Cortado para abreviar…>


}

 

Si te has mareado con tanto objeto, es entendible. Lo importante aquí es encargarse de liberar toda la memoria referenciada por los objetos de SharePoint.


Si estás desarrollado Web Parts, no puedes no leer el siguiente documento. Considéralo como lectura obligatoria para desarrollar web parts que vivan sin problemas. La dirección del documento en MSDN  es http://msdn2.microsoft.com/en-us/library/ms778813.aspx y éste detalla de forma casi perfecta como liberar toda la memoria no manejada cuando programas Web Parts.


¿Por qué no digo que está perfecto? Porque le faltó agregar parte del caso que vimos hoy. En ninguna parte del documento indica la liberación del objeto SPWeb que esta referenciado por SPListCollection y a su vez por SPList y finalmente por SPListItemCollection. Es cierto que podría subentenderse, pero no está explícito.


Eso sí, tiene gran detalle para mostrar con ejemplos, código que está mal escrito y como debe escribirse correctamente. Si pudiésemos contar siempre con ejemplos así de MSDN, sería fantástico. Incluso, da a conocer sutilezas de la implementación que pueden hacer que tu aplicación colapse si no sigues las recomendaciones. Como ejemplo, vean esta aclaración que aunque está en inglés, no es difícil de entender.

SPSiteCollection [ ] Index OperatorThe SPSiteCollection [] index operator returns a new SPSite object for each access. A SPSite instance is created even if that object has already been accessed.

Si bien hoy no hablamos de SPSite, la limpieza de éste es tan importante como la de SPWeb.

 

*Realmente no sé si las web parts corren sobre otra aplicación que no sea SharePoint [;)], ya sea en la versión para Windows 2003 conocida como Windows SharePoint Services, como la versión full llamada SharePoint Portal server (http://www.microsoft.com/latam/office/sharepoint/prodinfo/relationship.mspx).


Saludos,
Patrick

6 Replies to “Dispose en SPWeb, SPSite y SPListItemCollection, desarrollando Web Parts para SharePoint”

  1. I know this if off topic but I’m looking into starting my own weblog and was wondering what all is required to get set up? I’m assuming having a blog like yours would cost a pretty penny?
    I’m not very internet savvy so I’m not 100% sure. Any recommendations or advice would be greatly appreciated. Appreciate it

  2. We are a gaggle of volunteers and opening a new scheme in our community.
    Your web site offered us with valuable information to work on.

    You’ve performed a formidable task and our whole community will probably be grateful to you.

  3. I wanted to thank you for this fantastic read!! I certainly enjoyed every little bit of it.
    I have you saved as a favorite to check out new things you post…

Leave a Reply

Your email address will not be published. Required fields are marked *