c# - dotnet - Configuración de objetos a nulo/nada después de su uso en.NET
heap c# (14)
Algunos objetos suponen el método
.dispose()
que obliga a que el recurso se elimine de la memoria.
¿Debería establecer todos los objetos en
null
(
Nothing
en VB.NET) una vez que haya terminado con ellos?
Entiendo que en .NET es esencial deshacerse de cualquier instancia de objetos que implementen la interfaz
IDisposable
para liberar algunos recursos, aunque el objeto todavía puede ser algo después de que se
isDisposed
(de ahí la propiedad
isDisposed
en los formularios), por lo que supongo que puede ¿Todavía residen en la memoria o al menos en parte?
También sé que cuando un objeto queda fuera del alcance, se marca para la recopilación listo para la próxima pasada del recolector de basura (aunque esto puede llevar tiempo).
Por lo tanto, teniendo esto en cuenta, si se configura en
null
acelerará el sistema y se liberará la memoria, ya que no tiene que darse cuenta de que ya no está dentro del alcance y ¿existen efectos secundarios negativos?
Los artículos de MSDN nunca hacen esto en ejemplos y actualmente lo hago porque no puedo ver el daño. Sin embargo, me he encontrado con una mezcla de opiniones por lo que cualquier comentario es útil.
Creo que, por diseño de los implementadores de GC, no se puede acelerar GC con anulación. Estoy seguro de que preferirían que no te preocupes por cómo y cuándo funciona GC; trátalo de esta manera. Estar protegiéndote y vigilándote y cuidándote ... (se inclina, levanta el puño hacia el cielo) ... .
Personalmente, a menudo establezco explícitamente las variables en nulas cuando termino con ellas como una forma de autodocumentación. No declaro, uso, luego lo configuro como nulo más tarde - nulo inmediatamente después de que ya no sean necesarios. Estoy diciendo, explícitamente, "he terminado oficialmente contigo ... vete ..."
¿Es necesario anular en un lenguaje GC''d? No. ¿Es útil para el GC? Tal vez sí, quizás no, no lo sé con certeza, por diseño realmente no puedo controlarlo, e independientemente de la respuesta de hoy con esta versión, las futuras implementaciones de GC podrían cambiar la respuesta más allá de mi control. Además, si / cuando se optimiza la anulación es poco más que un comentario elegante si lo desea.
Me imagino que si aclaro mi intención al siguiente pobre tonto que sigue mis pasos, y si "podría" potencialmente ayudar a GC a veces, entonces vale la pena. Sobre todo me hace sentir ordenado y claro, ya Mongo le gusta sentirse ordenado y claro. :)
Lo veo así: Los lenguajes de programación existen para permitir que las personas le den a otras personas una idea de intención y un compilador una solicitud de trabajo sobre qué hacer (el compilador convierte esa solicitud en un idioma diferente (a veces varios) para una CPU) la CPU (s) podría indicar qué idioma usó, la configuración de las pestañas, los comentarios, el énfasis estilístico, los nombres de variables, etc.: la CPU tiene que ver con el flujo de bits que le indica qué registros y códigos de operación y ubicaciones de memoria deben dividirse. Muchas cosas escritas en código no se convierten en lo que consume la CPU en la secuencia que especificamos. Nuestro C, C ++, C #, Lisp, Babel, ensamblador o lo que sea es teoría en lugar de realidad, escrito como una declaración de trabajo. Lo que ves no es lo que obtienes, sí, incluso en lenguaje ensamblador.
Entiendo que la mentalidad de "cosas innecesarias" (como líneas en blanco) "no son más que ruido y un código de desorden". Ese fui yo antes en mi carrera; Lo entiendo totalmente. En esta coyuntura me inclino hacia aquello que aclara el código. No es como si estuviera agregando hasta 50 líneas de "ruido" a mis programas, son algunas líneas aquí o allá.
Hay excepciones a cualquier regla. En escenarios con memoria volátil, memoria estática, condiciones de carrera, singletons, uso de datos "obsoletos" y todo ese tipo de podredumbre, eso es diferente: NECESITA administrar su propia memoria, bloquearla y anularla a medida que la memoria no sea parte de El Universo GC''d - ojalá todo el mundo lo entienda. El resto del tiempo con los idiomas de GC''d es una cuestión de estilo en lugar de necesidad o un aumento de rendimiento garantizado.
Al final del día, asegúrese de entender qué es elegible para GC y qué no; bloquear, disponer y anular adecuadamente; encerar, encerar inhala exhala; y para todo lo demás digo: si se siente bien, hazlo. Su kilometraje puede variar ... como debería ...
Eche un vistazo a este artículo también: http://www.codeproject.com/KB/cs/idisposable.aspx
En su mayor parte, establecer un objeto en nulo no tiene ningún efecto. La única vez que debe asegurarse de hacerlo es si está trabajando con un "objeto grande", que tiene un tamaño mayor a 84K (como los mapas de bits).
En general no hay necesidad de establecer en nulo. Pero suponga que tiene una funcionalidad de reinicio en su clase.
Entonces puede hacerlo, ya que no desea llamar a dispose dos veces, ya que parte de Dispose puede no implementarse correctamente y lanzar la excepción System.ObjectDisposed.
private void Reset()
{
if(_dataset != null)
{
_dataset.Dispose();
_dataset = null;
}
//..More such member variables like oracle connection etc. _oraConnection
}
En general, no es necesario anular los objetos después del uso, pero en algunos casos me parece una buena práctica.
Si un objeto se implementa como IDisposable y se almacena en un campo, creo que es bueno anularlo, solo para evitar utilizar el objeto dispuesto. Los errores del siguiente tipo pueden ser dolorosos:
this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();
Es bueno anular el campo después de eliminarlo, y obtener un NullPtrEx justo en la línea donde se usa el campo nuevamente. De lo contrario, podría encontrarse con algún error críptico en la línea (dependiendo de lo que haga exactamente DoSomething).
Es probable que su código no esté lo suficientemente estructurado si siente la necesidad de
null
variables.
Hay varias formas de limitar el alcance de una variable:
Como lo menciona Steve Tranby
using(SomeObject object = new SomeObject())
{
// do stuff with the object
}
// the object will be disposed of
Del mismo modo, simplemente puede utilizar llaves:
{
// Declare the variable and use it
SomeObject object = new SomeObject()
}
// The variable is no longer available
Encuentro que usar rizos sin ningún "encabezado" para limpiar realmente el código y ayudar a hacerlo más comprensible.
Hay algunos casos en los que tiene sentido anular las referencias. Por ejemplo, cuando escribe una colección, como una cola de prioridad, y según su contrato, no debe mantener esos objetos vivos para el cliente después de que el cliente los haya eliminado de la cola.
Pero este tipo de cosas solo importa en colecciones de larga vida. Si la cola no va a sobrevivir al final de la función en la que fue creada, entonces importa mucho menos.
En general, no deberías molestarte. Deja que el compilador y GC hagan su trabajo para que puedas hacer el tuyo.
Karl tiene toda la razón, no es necesario que los objetos queden nulos después del uso.
Si un objeto implementa
IDisposable
, solo asegúrese de llamar a
IDisposable.Dispose()
cuando haya terminado con ese objeto (envuelto en un
try
...
finally
, o, un bloque
using()
).
Pero incluso si no recuerda llamar a
Dispose()
, el método finalizador en el objeto debería ser llamar a
Dispose()
por usted.
Pensé que este era un buen tratamiento:
y esto
No tiene sentido tratar de adivinar el GC y sus estrategias de administración porque es autoajustable y opaco. Hubo una buena discusión sobre el funcionamiento interno con Jeffrey Richter en Dot Net Rocks aquí: Jeffrey Richter en el modelo de memoria de Windows y el libro de Richters CLR a través del capítulo 20 de C # tiene un excelente tratamiento:
La única vez que debe establecer una variable en nula es cuando la variable no sale del ámbito y ya no necesita los datos asociados. De lo contrario no hay necesidad.
No, no null objetos. Puede consultar http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx para obtener más información, pero configurar cosas a nulo no hará nada, excepto sucio tu código.
Otra razón para evitar que los objetos queden nulos cuando haya terminado con ellos es que realmente pueden mantenerlos con vida por más tiempo.
p.ej
void foo()
{
var someType = new SomeType();
someType.DoSomething();
// someType is now eligible for garbage collection
// ... rest of method not using ''someType'' ...
}
permitirá que el objeto referido por someType sea GC''d después de la llamada a "DoSomething" pero
void foo()
{
var someType = new SomeType();
someType.DoSomething();
// someType is NOT eligible for garbage collection yet
// because that variable is used at the end of the method
// ... rest of method not using ''someType'' ...
someType = null;
}
A veces puede mantener el objeto vivo hasta el final del método. El JIT generalmente optimiza la asignación a nulo , por lo que ambos bits de código terminan siendo los mismos.
Stephen Cleary explica muy bien en este post: ¿Debo establecer variables en nulo para ayudar a la recolección de basura?
Dice:
La respuesta corta, para el impaciente Sí, si la variable es un campo estático, o si está escribiendo un método enumerable (con rendimiento) o un método asíncrono (usando async y a la espera). De otra manera no.
Esto significa que en los métodos regulares (no enumerables y no asíncronos), no establece variables locales, parámetros de método o campos de instancia en nulo.
(Incluso si está implementando IDisposable.Dispose, aún no debe establecer variables en nulo).
Lo importante que debemos tener en cuenta son los campos estáticos .
Los campos estáticos siempre son objetos raíz , por lo que el recolector de basura siempre los considera "vivos" . Si un campo estático hace referencia a un objeto que ya no es necesario, debe establecerse en nulo para que el recolector de basura lo considere como elegible para la recolección.
Establecer campos estáticos en nulo no tiene sentido si todo el proceso se está cerrando. Todo el montón está a punto de ser basura recolectada en ese punto, incluyendo todos los objetos raíz.
Conclusión:
Campos estáticos ; Eso es todo. Cualquier otra cosa es una pérdida de tiempo .
También:
using(SomeObject object = new SomeObject())
{
// do stuff with the object
}
// the object will be disposed of
este tipo de "no hay necesidad de establecer objetos en nulo después de su uso" no es del todo exacto. Hay veces que necesita NULL la variable después de desecharla.
Sí, SIEMPRE debe llamar a
.Dispose()
o
.Close()
a cualquier cosa que lo tenga cuando haya terminado.
Ya se trate de manejadores de archivos, conexiones de bases de datos u objetos desechables.
Aparte de eso es el patrón muy práctico de LazyLoad.
Digamos que tengo e instanciado
ObjA
de la
class A
Class A
tiene una propiedad pública llamada
PropB
de la
class B
Internamente,
PropB
usa la variable privada de
_B
y su valor predeterminado es nulo.
Cuando se
PropB.Get()
, verifica si
_PropB
es nulo y, si lo es, abre los recursos necesarios para crear una instancia de
B
en
_PropB
.
A continuación, devuelve
_PropB
.
Para mi experiencia, este es un truco realmente útil.
La necesidad de anular es si restablece o modifica A de alguna manera que el contenido de
_PropB
fue el elemento secundario de los valores anteriores de
A
, deberá
_PropB
Y
_PropB
para que LazyLoad pueda reiniciar para obtener el valor correcto SI el código lo requiere.
Si solo hace
_PropB.Dispose()
y poco después espera la comprobación nula para que LazyLoad tenga éxito, no será nula y verá datos obsoletos.
En efecto, debe anularlo después de
Dispose()
solo para estar seguro.
Desearía que fuera de otra manera, pero ahora tengo un código que muestra este comportamiento después de un
Dispose()
en un
_PropB
y fuera de la función de llamada que hizo el Dispose (y por lo tanto casi fuera de alcance), la propiedad privada todavía no está No nulo, y los datos antiguos todavía están allí.
Eventualmente, la propiedad dispuesta se anulará, pero eso no ha sido determinista desde mi perspectiva.
La razón principal, como alude dbkk, es que el contenedor principal (
ObjA
con
PropB
) mantiene la instancia de
_PropB
en el alcance, a pesar de
Dispose()
.