videos trabajando servicio sacar saca recolectores recolección recoleccion recogida que pasa mañana los hay feriados dias debe camion basura c garbage-collection

trabajando - ¿Por qué usar el recolector de basura?



servicio de recolección de basura (22)

Entonces, ¿por qué usar GC, cuando todo lo que necesita hacer es en realidad ser prudente con la asignación / desasignación de memoria?

El problema es que se vuelve excepcionalmente difícil ser lo suficientemente sabio. Falla en las apuestas de sabiduría y obtienes una pérdida de memoria o un fallo. Aquí está la guía rápida en maceta de la sabiduría aplicada por computadora en la administración de memoria automatizada.

Si tienes un programa simple (el nivel cero de complejidad), puedes usar la asignación basada en la pila para manejar todo. Es muy fácil obtener la asignación de memoria de esta manera, pero también es un modelo de cálculo muy restringido (y también tiene problemas con el espacio de pila). Así que empiezas a usar el montón; ahí es donde comienza la "diversión".

El primer nivel de complejidad es donde tienes punteros cuya vida útil está vinculada a un marco de pila. Una vez más, es bastante simple de hacer y constituye la base de mucha programación en C ++.

El segundo nivel de complejidad es donde tienes el conteo de referencias. Esta es la base para los punteros inteligentes de C ++ y es bastante buena para manejar casi todo, hasta un bosque de gráficos acíclicos dirigidos. Puede lograr mucho con esto, y permite algunos modelos de computación que funcionan bastante bien con la programación funcional y la programación en paralelo también.

Más allá de eso está el tercer nivel, la recolección de basura. Esto puede manejar gráficos arbitrarios de estructuras de memoria, pero con el costo de tener más hambre de memoria (ya que, en general, no intenta desasignarse tan pronto). Uno de los costos principales es que la cantidad de memoria asignada tiende a ser mayor, debido al hecho de que es solo después del punto en el que pudo haber eliminado la memoria que realmente se convierte en elegible para la eliminación automática. fuera de las vidas.

Posible duplicado:
Recolección de basura en C ++ - ¿por qué?

Hola, leí algunos artículos sobre recolectores de basura, y aún hay una cosa que no entiendo: ¿por qué usar la recolección de basura?

Intentaré explicar mis pensamientos:

El recolector de basura debe liberar la memoria asignada dinámicamente al sistema en caso de que no sea necesaria, ¿verdad? Entonces, si escribes un programa en lenguaje C, sabes si necesitas algo de memoria, así que si no lo haces, simplemente puedes destruirlo.

Entonces, ¿por qué usar GC, cuando todo lo que necesita hacer es en realidad ser prudente con la asignación / desasignación de memoria? ¿O me estoy perdiendo algo? Gracias.


Entonces, si escribes un programa en lenguaje C, sabes si necesitas algo de memoria, así que si no lo haces, simplemente puedes destruirlo.

Esa es la teoría, al menos. El problema es que puede complicar mucho el código. Por ejemplo, esto:

for (x in ComputeBigList()) ...

se convierte en esto

var xs = ComputeBigList(); try { for(x in xs) ... } finally { FreeMemory(xs); }

La falta de un recolector de basura nos obligó a nombrar el resultado de ComputeBigList , almacenarlo en una variable y luego agregar una instrucción de eliminación envuelta en un finally , solo para asegurarnos de que realmente se eliminó.

Aquí es donde los fanáticos de C ++ deben señalar que las llamadas destructoras garantizadas de C ++ pueden hacer esto mucho más fácil. Dicho esto, luego tiene la sobrecarga y el código adicional asociado con el recuento de referencias, etc., asumiendo que desea que sus objetos puedan escapar de la extensión dinámica en la que fueron creados. (es decir: asigno un objeto y luego lo devuelvo.)

La otra cosa que hace GC que es útil es controlar cómo usas tu memoria. Un GC reubicado le permite organizar los objetos para que se pueda acceder a ellos de manera más eficiente. En general, GC le da a su tiempo de ejecución un poco más de flexibilidad cuando paga el precio de reclamar memoria. (Las actualizaciones explícitas de liberaciones y refcount siempre tienen que ser inmediatas).


sabes si necesitas algo de memoria, así que si no, simplemente puedes destruirlo.

Podría usar un argumento similar para justificar casi cualquier dispositivo de ahorro de trabajo. ¿Por qué escribir expresiones matemáticas cuando solo puedes producir lenguaje ensamblador? ¿Por qué usar caracteres legibles cuando puedes usar binarios?

La razón es simple. Trabajo con programadores que son algunos de los mejores en su campo. Puedo decir sin temor a exagerar que algunos de ellos han escrito el libro en su campo. Y sin embargo, estas personas están programando en C ++ y cometen errores con la administración de la memoria. Cuando cometen estos errores, son particularmente difíciles de encontrar y corregir. ¿Por qué las personas increíbles cuyos talentos podrían ser dirigidos a otra parte pierden el tiempo haciendo algo que una máquina podría hacer mejor?

(Y sí, hay buenas respuestas a esta pregunta. Por ejemplo, cuando cada byte de memoria en su cuenta y por lo tanto no puede permitirse el lujo de tener basura en cualquier momento. Pero ese no es el caso en general).


Considere un caso donde un puntero particular es usado por dos subsistemas separados. Un subsistema se puede hacer con la variable y el programador puede pensar: "Ya terminé con esto, simplemente seguiré adelante y lo liberaré", sin saber que otro subsistema aún necesita acceso a él. O otro escollo, el desarrollador piensa: "No estoy seguro de si hay otro subsistema que pueda necesitar esto" (incluso si no lo hay), lo que lleva a pérdidas de memoria. Este tipo de situación aparece mucho en los sistemas complejos.


Cuando escribo programas, me gusta concentrarme en el dominio de mi problema y no en los detalles de los detalles de implementación no relacionados. Por ejemplo, si escribo, por ejemplo, un servidor web, mi dominio de problemas son las conexiones de red, los protocolos, la transmisión / recepción de datos, etc., no la asignación de memoria y la desasignación. Si estoy escribiendo un videojuego, mi dominio de problemas es el rendimiento gráfico y quizás la IA y, de nuevo, no la asignación de memoria y la desasignación.

Cada vez que me dedico a trabajar en cosas que no forman parte de mi dominio de problemas, es un tiempo perdido que se podría gastar en mi dominio de problemas. Al concentrarme en los detalles de bajo nivel, en otras palabras, la calidad de mi trabajo real, el problema que realmente estoy tratando de resolver, sufre.

Además, su bit "todo lo que necesita hacer es, en realidad, ser prudente con la asignación / desasignación de memoria" solo sirve para resaltar dos posibilidades (que puedo pensar, al menos):

  1. Eres muy inexperto.
  2. Usted ha estado - probablemente inconscientemente - paralizando sus diseños para mantenerlos dentro de las restricciones de sus suposiciones simplistas sobre la administración de la memoria.

La gestión de la memoria en el software del mundo real es una tarea decididamente no trivial. Las complejas estructuras de datos típicas de cualquier pieza importante de software moderno conducen a una ofuscación increíble en términos de determinar la "vida" y la "propiedad" de cualquier pieza dada de memoria asignada dinámicamente. Esto se hace aún más complejo (potencialmente por órdenes de magnitud) con la introducción de subprocesos o, peor aún, multiprocesamiento (simétrico y de otro tipo) con cualquier forma de estado compartido.

No es casualidad que los errores más comunes en el software escrito en entornos de memoria no administrados estén relacionados con la memoria asignada de forma dinámica y mal administrada.

Entonces, ¿por qué usar la recolección de basura? Porque no es tan inteligente como cree que es cuando trata con los caprichos de la memoria asignada dinámicamente. Realmente no lo eres. No importa lo inteligente que pienses que eres. Si reconoce que no es tan inteligente, paralizará sus diseños para que la administración de su memoria sea lo suficientemente simple como para comprender. Sin embargo, si crees arrogantemente que puedes lidiar con cualquier cosa, simplemente atornillas a los usuarios que tienen que lidiar con tu software de mierda, propenso a fallas y / o memoria.


Cuando no tiene que hacer una aplicación en tiempo real (no puede estar seguro de cuándo el recolector de basura hará su evento de trabajo si lo obliga) o cuando no le importa controlar completamente su memoria, puede desarrollar la Libere la cabeza y casi asegúrese de no hacer una pérdida de memoria.


Cuando trabaja en proyectos complejos con múltiples llamadas a bibliotecas y código externo que no escribió, es muy difícil hacer un seguimiento de los objetos que necesita liberar y los objetos liberados por librerías externas y otros lugares en su código.

Existen muchas herramientas que ahora hacen que la tarea de localizar las fugas de memoria sea más fácil, pero tienden a ser errores insidiosos que solo se vuelven perceptibles después de que el sistema haya estado funcionando durante horas o días.

Sin embargo, estoy de acuerdo con su sentimiento. Si tengo control sobre el código base, prefiero escribir algo donde esté a cargo (como c). Pero si tengo que trabajar con fuerzas externas, algo con un recolector de basura decente es mucho más atractivo.


En estos días, la mayoría de las personas que usan un recolector de basura lo hacen dentro de un entorno administrado (como la Máquina Virtual de Java o el Tiempo de ejecución de lenguaje común de .NET). Estos entornos administrados agregan una arruga adicional: restringen la capacidad de llevar punteros a las cosas . En el CLR, por ejemplo, hay una noción de un puntero (que puede usar a través del IntPtr administrado o el bloque de código unsafe no administrado), pero hay condiciones limitadas en las que puede usarlas. En la mayoría de los casos, tiene que "fijar" los objetos correspondientes en la memoria para que el GC no los mueva mientras está trabajando con sus punteros.

¿Por qué esto importa? Porque, como resultado, un asignador administrado que tiene permitido actualizar punteros y mover objetos en la memoria puede ser mucho más eficiente que un asignador de estilo malloc . Puede hacer cosas geniales, como la recolección de basura generacional , que hace que las asignaciones de pila sean tan rápidas como las asignaciones de pila , puede perfilar el comportamiento de memoria de su aplicación mucho más fácilmente y, oh sí, también puede detectar fácilmente objetos sin referencias y liberarlos automáticamente.

Por lo tanto, no solo se trata de una mayor productividad de los programadores (aunque si le pregunta a alguien que trabaja en un lenguaje administrado, dará fe de la mayor productividad que les brinda), también se trata de habilitar tecnologías de programación completamente nuevas.

Finalmente, la recolección de basura se vuelve realmente necesaria cuando se trabaja con lenguajes de programación funcionales (o programación en estilos funcionales). De hecho, el primer recolector de basura fue inventado por McCarthy en 1959 como parte del desarrollo del lenguaje Lisp. La razón es doble: primero, la programación funcional fomenta estructuras de datos inmutables, que son más fáciles de recopilar, y segundo, en la programación funcional pura no hay función de asignación; la memoria siempre se asigna como "pila" (funciones locales) y luego se mueve a un "montón" si es capturada por un closure . (Esto es una simplificación excesiva, pero sirve para ilustrar el punto.)

Entonces ... si estás programando en un estilo imperativo, y eres "lo suficientemente inteligente" para hacer lo correcto con todas tus asignaciones de memoria, no necesitas recolección de basura. Pero si desea cambiar su estilo de programación para aprovechar los últimos avances en tecnología de programación, probablemente le interese utilizar un recolector de basura.


Es un mecanismo anti-tonto-programador . Y créanme, cuando el código se vuelve muy complejo, cuando pensamos en términos de memoria asignada dinámicamente, todos somos igualmente tontos.

En mi corta experiencia como programador, he pasado (acumulado) días tratando de averiguar por qué valgrind (u otras herramientas similares) está reportando pérdidas de memoria, cuando todo estaba "tan bien codificado".


Estoy de acuerdo con el comentario de mouviciel. Pero los recolectores de basura permiten un desarrollo más rápido porque el desarrollador ya no tiene que preocuparse por las fugas de memoria, lo que les permite centrarse en otros aspectos de su programa.

Pero tenga en cuenta que si está programando en un lenguaje que tiene recolección de basura, es muy sabio estar consciente de ese hecho. Es casi un deber (IMO) entender cómo funciona y qué está haciendo en el fondo.


Las recolecciones de basura pueden ser más eficientes.

Para asignar memoria, malloc necesita moverse para encontrar un espacio contiguo de memoria lo suficientemente grande. Con un recolector de basura de compactación, asignar memoria es golpear un puntero (o cerca de él)

En C ++, puede manejar la memoria de forma segura y limpia en muchas situaciones sin un recolector de basura utilizando punteros inteligentes y siguiendo estrictamente las convenciones. Pero (1) esto no funciona en todas las situaciones, incluso con shared_ptr y weak_ptr, y (2) el conteo de referencias requiere coordinación entre los subprocesos, que tiene una penalización de rendimiento.

La usabilidad es una preocupación más importante, pero la recolección de basura es, a veces, más rápida que liberar memoria de manera determinista.


Liberar la memoria que ya no se necesita es un objetivo ideal, pero no es posible hacerlo automáticamente en toda la generalidad. Incluso en ausencia de una entrada externa (que puede afectar si se necesitará o no parte de los datos), decidir, dado el estado completo de la memoria y el código completo, si se necesitará parte de la memoria es equivalente a la detención Problema , que es imposible de resolver para una computadora.

No hace falta decir que el mismo problema también supera las capacidades del cerebro programador promedio bastante rápido, a medida que aumenta el tamaño de la aplicación. En la práctica, se puede lograr una administración de memoria perfectamente correcta solo en dos situaciones:

  1. el problema es simple (por ejemplo, la aplicación de línea de comandos de corta duración) y el programador lo suficientemente disciplinado;
  2. El programador es Donald Knuth .

En todos los demás casos, tenemos que usar aproximaciones. Un recolector de basura se basa en la siguiente aproximación: detecta bloques de memoria inalcanzables. No puede saber si se usará o no un bloque accesible, pero no se usará un bloque inalcanzable (porque usar implica alcanzar). Otra aproximación común (utilizada por muchos programadores que creen que son lo suficientemente inteligentes) es asumir que pensaron en cada bloque y luego orar por lo mejor (una variante: educar a los usuarios para que crean que las fugas de memoria son una característica, y que un reinicio de vez en cuando es normal).


No necesita recolección de basura si no produce basura en primer lugar.

Una forma de evitar la basura es no usar la asignación de memoria dinámica . La mayoría de los programas integrados no utilizan la asignación de memoria dinámica. Incluso cuando se usa la asignación de memoria dinámica (incluso en muchos programas de PC) a menudo no hay una razón real para usarla. (El hecho de que la asignación de memoria dinámica sea posible no significa que deba usarse en todas partes).

Otra forma de evitar la recolección de basura es usar un lenguaje que no separe la referencia de los contenidos . En ese caso, la pérdida de memoria real ni siquiera es posible. (Pero, por supuesto, todavía es posible usar demasiada memoria). En mi humilde opinión, los idiomas de alto nivel no deben meterse con "punteros" (variables de dirección) en absoluto.


Para evitar errores. No importa lo cuidadoso que sea para desasignar la memoria, o bien eventualmente cometerá un error o eventualmente codificará un programa que requiere un complejo patrón de referencia de memoria que hará que la probabilidad de error sea mucho mayor.

Cualquier posibilidad que exista durante el tiempo suficiente se hará realidad, eventualmente perderá memoria con métodos manuales, a menos que se ponga un esfuerzo adicional en controlar el consumo de memoria. Este esfuerzo extra roba tiempo de la codificación hacia el propósito principal del programa, que probablemente no sea para administrar la memoria.

Además, incluso si su programa no pierde memoria, la recolección de basura a menudo tiende a manejar la memoria de manera más eficiente que muchos otros métodos de recolección de basura. La mayoría de las personas no hacen new bloques de objetos para evitar múltiples llamadas new , ni tampoco volverían a visitar y limpiarían la memoria caché de los new objetos de edición no utilizados posteriormente. La mayoría de los métodos de recolección de basura manual se concentran en liberar memoria en los límites del bloque, lo que podría permitir que la basura permanezca un poco más de lo necesario.

Cada beneficio y característica adicionales que acumula en la recolección de basura manual lo lleva un paso más cerca de la recolección automática de basura. No usar las utilidades para recolectar basura más allá de las llamadas manuales para liberarla no se escalará fácilmente. O bien, pasará mucho tiempo revisando la asignación / reclimación de la memoria, o no gastará lo suficiente para evitar una pérdida de memoria.

De cualquier manera, la recolección automática de basura resuelve este problema para usted, permitiéndole regresar al punto principal de su programa.


Para ser más productivo. En otras palabras, el programador puede concentrarse en escribir los bits que son únicos para su problema particular.


Porque no somos lo suficientemente sabios.


Porque ya no estamos viviendo a principios de los 80s. Es una pérdida de tiempo para los desarrolladores y es simplemente molesto preocuparse por las tareas de nivel más bajo cuando estás a punto de crear una aplicación increíble.


Puede necesitar liberar recursos de interoperabilidad lo antes posible (archivo bloqueado). Gc.Collect puede garantizar que los objetos COM se liberen (si no se hace referencia a ellos).

Si realiza una vista previa de impresión, esto requiere 2 manejadores Gdi para cada página (imagen + metarchivo). Estos recursos no son liberados por PrintDocument o PrintControler, están esperando a GC.

Probé en un programa interactivo para usar Gc.Collect cuando el usuario regresa al menú principal. Con esta operación, la memoria notificada para el Administrador de tareas es aproximadamente el 50%.

Creo que esto no es importante, pero codificar un Gc.Collect cuando se sabe que no se hace referencia a una gran cantidad de memoria es una opción simple.


Puedes hacer tu propia recolección de basura, como mencionaste. Agregar un recolector de basura simplemente lo libera de tener que preocuparse por eso y de tener que tomarse el tiempo para escribir y probar su código de recolección de basura. Si está trabajando en una base de código existente que contiene pérdidas de memoria, puede ser más fácil (y más efectivo) usar un recolector de basura automático que tratar de aprender todo el código existente con suficiente detalle para encontrar y solucionar los problemas.

Dicho esto, no soy un fanático de agregar facilidades de recolección automática de basura a los idiomas que no lo tienen incorporado. Si el lenguaje fue diseñado asumiendo que el desarrollador estaría atento a la asignación de memoria y la desasignación, entonces (IMHO ) hace un daño al desarrollador para eliminar esta responsabilidad de ellos. No poder controlar con precisión cuándo y cómo se libera la memoria puede llevar a un comportamiento inconsistente. Pensar y definir toda la vida útil de la memoria de asignación dinámica es una parte importante de la planificación de su código. Ningún sistema automatizado es un verdadero sustituto de la programación cuidadosa y precisa (que se aplica a mucho más que los recolectores de basura).


Sin un recolector de basura, cada vez que asigne algo dinámicamente, debe realizar un seguimiento de cuándo ya no lo necesita y destruirlo solo después de que ya no lo necesite. Esto puede ser difícil, especialmente cuando / si varias partes diferentes del programa tienen punteros a un objeto, y ninguno de ellos sabe qué otro código podría estar usándolo.

Esa es la teoría de todos modos. En realidad, debo admitir que tampoco me ha funcionado de esa manera. En particular, cuando (la mayoría) las personas saben que su código utilizará un recolector de basura, tienden a descartar que la administración de la memoria no sea un problema o incluso un problema a considerar. Como resultado, pueden saltar y comenzar a codificar más rápidamente. Para los problemas pequeños que entienden bastante bien antes de comenzar, esto puede ser una victoria importante, pero para problemas más grandes, me parece (al menos para mí) que la tendencia es saltar y comenzar a escribir código antes de que realmente entiendan el problema.

En mi experiencia, la falta de un recolector de basura hace que los desarrolladores piensen un poco más en los problemas de por vida. En el proceso, están motivados para encontrar soluciones simples a los problemas de la vida útil de los objetos, y por lo general hacen exactamente eso. En el proceso, generalmente han simplificado el código en general (no solo la administración de la memoria) hasta el punto de que es mucho más simple, más limpio y más comprensible.

En cierto modo, me recuerda mucho a la vieja parábola de dos programadores . Al final de un proyecto, los gerentes que miran el código que usó la recolección de basura piensan que es realmente bueno usar la recolección de basura. El problema es claramente más complejo de lo que se dieron cuenta, y dada la complejidad del código y los problemas de la vida útil, no hay forma de que alguien pueda realizar un seguimiento de ellos a mano y producir un código que esté incluso cerca de estar libre de fugas.

Al final de hacer lo mismo sin la recolección de basura, la reacción es más bien la opuesta. Se dan cuenta de que el problema (en general) es en realidad mucho más simple de lo que creían. Los problemas de la vida útil de los objetos no son realmente tan complejos como lo esperaban, y producir un código libre de fugas no fue particularmente desafiante.


Solo tiene que ser sabio, eso es correcto;) Sin embargo, si su diseño no es correcto, podría ser fácil supervisar algo.

Con la recolección de basura, sin embargo, no tiene que preocuparse por la memoria y puede centrarse más en el resto de su programa, por lo que posiblemente desarrolle "más rápido"


La gestión de la memoria es un problema de implementación , que no está conectado con el objetivo del programa.

Por objetivo del programa me refiero a cosas como la lógica de negocios.

cuando trabaja en problemas de implementación, dedica su tiempo y esfuerzos a cosas que no le ayudan a terminar el programa.