entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

2votos

Declarar una variable dentro de un bucle

Desde el punto de vista de la memoria. ¿Cómo creéis que se comporta esto?

void MemoryLeakTest()
{
   for (int i = 0; i < 1000000; i++)
   {
      TimeSpan intervalo = new TimeSpan(2, 0, 0); // 2 horas
      Console.WriteLine("Nueva fecha: {0}", 
                        DateTime.Now.Add(intervalo));
   }       
}

Preguntas:

  • ¿Confío en el Recolector de Basura para que cada cierto número de iteraciones me vaya borrando de la memoria todas las instancias de "intervalo" que ya no uso?
  • ¿Debería hacer una llamada al Recolector de Basura para que haga "limpieza" y así quitar todas las instancias de "intervalo" que ya no se están usando?
    • ¿Debería de declarar "intervalo" fuera del bucle?
    • ¿Confío en que el compilador optimice mi código y declare la variable fuera del bucle?

Pregunta extra:
Y si en lugar de ser una variable de tipo instancia de clase fuese una variable de tipo valor como por ejemplo un entero. ¿Aplicaría lo mismo, o me quedaría sin memoria en la pila de programa?

5 Respuestas

1voto

JoanPerez Puntos770

Todo depende del compilador, pero normalmente cuando se abandona el ámbito en el que fue definido cualquier clase/objeto/variable automáticamente se libera el espacio que éste ocupaba.

En cuanto al recolector de basura, lo que hace básicamente el recolector de basura es agrupar los objetos con los huecos que precisamente ha dejado los otros objetos que han dejado de existir para poder tener más espacio. En java, por ejemplo, el recolector de basura es llamado automáticamente y nosotros solo podemos indicarle cuando es un buen momento para hacer esta operación, pero no asegura que esto vaya a pasar.

0voto

oscar_arrivi comentado

Efectivamente el recolector me va a limpiar la memoria cuando crea conveniente. A lo mejor la primera vez que recolecte se encuentra con 300 TimeSpan, la segunda con 800, la tercera... Así que imagino que si viera una gráfica de memoria vería como las almenas de un castillo: un sube y baja.
Pero si en vez de un TimeSpan, ¿fuera una clase pesada? ¿Me fio del optimizador? O tengo que esperar al recolector.
A lo mejor es que simplemente es de cajón que ese tipo de cosas son responsabilidad del programador definirlas fuera del bucle.
¿Alguien me lo aclara?

2votos

aitor_aznar comentado

Como dice Joan Pérez, las variables se desechan (por el recolector de basura) cuando se sale del ámbito en el que están declaradas, así pasa en Java.

Y si vas a usar una y otra vez la misma variable (sumar 2 horas), la lógica recita que se declare una sola vez y siempre fuera del bucle.

1voto

oscar_arrivi comentado

Al final he construido un proyectito de Consola para probar todo esto y he llegado a las siguientes conclusiones:

  • Si la variable es una instancia de clase, es indiferente que se define tanto fuera como dentro del bucle, puesto que en ambos casos se va a crear una nueva instancia de clase en cada iteración.
  • Si la variable es de tipo valor, como por ejemplo un double, el comportamiento mostrado en el profiler es el mismo. Lo cual es lógico porque si se define dentro del bucle, la variable se libera al salir del ámbito de cada iteración, y si se define fuera pues se reutiliza la misma.

Quizás en términos de memoria no haya diferencias pero si que puede haber algo respecto al rendimiento. Al definirla dentro del bucle, el framework tendrá más trabajo al tener que liberar la memoria y volver a reservarla en cada iteración.
Por tanto, creo que la mejor opción es siempre definir la variable fuera del bucle aunque si se hace dentro, tampoco sería una penalización grande.

1voto

Hola

  • Cada llamada al recolector gasta unos recursos de procesamiento, la llamadas suelen hacerse previas a tareas pesadas en las que necesites todos los recursos disponibles.
  • Si quieres que tu variable exista en el ámbito del bucle, la declaras dentro, si la vas a usar fuera del bucle, la declaras fuera.

Saludos.

0voto

oscar_arrivi comentado

Todo lo que dices es cierto, pero no veo que hayas respondido a las preguntas.
Mi intención no es estar llamando al recolector cada vez que vea que una variable ya no se va a utilizar, sino dejar a .NET que lo use cuando vea que es más oportuno (y seguro) hacerlo.
Lo que quiero saber si el código del ejemplo me va a causar un problema de memoria si ejecuto ese código miles de veces en mi programa. Supongo que el recolector de basura de vez en cuando hará limpieza, por lo que no creo que me cuelgue mi programa. O si bien, el compilador me optimizará ese código como si lo hubiera definido fuera del código y no hay ningún problema.

1voto

juanvalert Puntos200

No creo que ese codigo te vaya a dar problemas ya que es un tipo de datos nativo de .NET. Como bien comentáis en el resto de respuestas, .NET liberara esas variables en el momento en el que la aplicación sale del ámbito de esa variable.

El único apunte que haría yo sería en cuanto a una clase definida por nosotros. Depende que tipo de recursos uses dentro de esa clase (un array de bytes por ejemplo), sería necesario hacer un destructor de la clase y implementar la interfaz IDisposable con el método Dispose(), para asegurarte que se liberan todos los recursos usados cuando la variable deja de ser usada.

1voto

isanz Puntos210

Hola Oscar Arrivi, primero voy a intentar contestara a tus preguntas segun lo veo yo.

  • ¿Confío en el Recolector de Basura para que cada cierto número de iteraciones me vaya borrando de la memoria todas las instancias de "intervalo" que ya no uso?

El recolector de basura depende de muchas variables para que realice la "limpieza", es por ello que "popularmente" se considera que actua aleatoriamente, es decir, es muy dificil predecir cuando va a realizar esa limpieza. Por otra parte, el recolector de basura solo recoge los objetos y libera esa memoria de los que quedan "inalcanzables" para el programa, eso quiere decir basicamente que libera la memoria ocupada por objetos que han dejado de ser referenciados (como haces tu en cada new TimeSpan, el anterior queda sin referencia y sera recolectado).

  • ¿Debería hacer una llamada al Recolector de Basura para que haga "limpieza" y así quitar todas las instancias de "intervalo" que ya no se están usando?

Si para ti es importante dicha limpieza se puede obligar al recolector a actuar. Eso es mas largo de explicar pero creo que viene bien explicado aqui:

http://ayddup.wordpress.com/2011/08/08/la-memoria-en-java-garbage-collector-y-el-metodo-finalize/

  • ¿Debería de declarar "intervalo" fuera del bucle?
    Si. Ademas es una buena practica de programacion.

  • ¿Confío en que el compilador optimice mi código y declare la variable fuera del bucle?
    Las ayudas en la programacion estan muy bien, pero no olvides que la primera responsabilidad de hacer bien el trabajo es tuya. Nunca dejes en manos del compilador algo que no tienes claro que haga y mucho mas si tu tienes conocimiento de que es asi. Es mejor acostumbrarse con las buenas practicas que dejar en manos de otros nuestras obligaciones. Respecto a eso, tampoco esta tan claro que el compilador haga eso ni que lo haga siempre o siempre que tu creas que lo va a hacer. Para no llevarte sorpresas yo no confiaria.

Y si en lugar de ser una variable de tipo instancia de clase fuese una variable de tipo valor como por ejemplo un entero. ¿Aplicaría lo mismo, o me quedaría sin memoria en la pila de programa?

Lo primero, un objeto no es un tipo de dato primitivo. Ambos reservan memoria pero la diferencia es que con el primero se pueden crear multiples instancias(con lo que se reduciria la cantida de memoria a cada nueva instancia), mientras que con los tipos de datos primitivos se reserva memoria y lo unico que haria seria cambiar el valor al que apunta esa variable.

Espero haberte sido de ayuda.
Un saludo.

1voto

sergio.uriel Puntos280
  • Sí puedes "confiar" en el GC ya que explícitamente tu objetos TimeSpan sólo existen dentro del bucle, es fácil calcular el ciclo de vida de tu objeto ya que únicamente lo usas dentro.
  • No deberías de hacer una llamada al GC ya que esas llamadas "cuestan" y tu no tienes manera de decirle al GC que sólo se encargue de "limpiar" tu objeto, el GC "limpiaría" lo que el considere.
  • Sí deberías de declarar "intervalo" fuera del bucle ya que no tiene sentido crear un objeto nuevo cada iteración que siempre será exactamente igual, podrías considerarlo como un valor constante.
  • Independientemente si el compilador optimice tu código o no, no deberías de asumir lo que el compilador podría hacer ya que estás programando en alto nivel, esperar que el compilador optimice el código no es una buena práctica, es mejor hacer las buenas prácticas uno mismo.
  • Si en lugar de ser un objeto fuera un valor primitivo como un entero, yo te recomendaría que lo declararas dentro del bucle pero como constante ya que así le estás diciendo explícitamente cómo hacer las cosas al compilador. No te quedarías sin memoria, no veo por qué podría pasar, ya que un valor primitivo utiliza mucho menos memoria que un TimeSpan.

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta