Cómo optimizar el uso de la memoria en C# | Mark Farragher | Skillshare

Velocidad de reproducción


  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Cómo optimizar el uso de la memoria en C#

teacher avatar Mark Farragher, Microsoft Certified Trainer

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Ve esta clase y miles más

Obtenga acceso ilimitado a todas las clases
Clases enseñadas por líderes de la industria y profesionales activos
Los temas incluyen ilustración, diseño, fotografía y más

Lecciones en esta clase

17 lecciones (2h 59min)
    • 1. Optimizar el uso de la memoria en C#

      2:37
    • 2. Introducción a . Gestión de memoria de memoria de redes de

      3:44
    • 3. ¿Qué es la memoria?

      5:23
    • 4. ¿Qué es la memoria de la heap heap

      4:59
    • 5. ¿Cómo se almacenan tipos de valores en la memoria?

      5:42
    • 6. ¿Cómo se almacenan tipos de referencia en la memoria?

      5:29
    • 7. ¿Qué es el boxeado y boxing

      6:03
    • 8. Cómo funciona de la colección de garabatos. ¿NET? En

      16:46
    • 9. Consejo #1: optimiza tu código para la colección de garabatos

      18:20
    • 10. Consejo #2: evitar los finalizadores de clase

      17:11
    • 11. Consejo #3: utiliza el patrón de desequilibrio

      16:34
    • 12. Consejo #4: evitar el boxeado y el boxing

      9:51
    • 13. Consejo #5: no añadir muchos cuerdas

      16:08
    • 14. Consejo #6: utiliza obras en lugar de las clases

      17:55
    • 15. Consejo #7: preasignar colecciones

      12:30
    • 16. Consejo #8: no te materiales expresiones de LINQ a tiempo

      18:04
    • 17. recap de cursos

      1:44
  • --
  • Nivel principiante
  • Nivel intermedio
  • Nivel avanzado
  • Todos los niveles

Generado por la comunidad

El nivel se determina según la opinión de la mayoría de los estudiantes que han dejado reseñas en esta clase. La recomendación del profesor o de la profesora se muestra hasta que se recopilen al menos 5 reseñas de estudiantes.

63

Estudiantes

--

Proyectos

Acerca de esta clase

3bb6dc07

Los computadores modernos tienen muchos memoria. Pero es muy fácil grabarlo en este segundo de tiempo si tu código no es eficiente de asignar memoria.

Sabías que un error puede que tu código asigne 1600 de memoria

No sea "el desarrollador que siga cerrando el servidor de desarrollo con una excepción de OutOfMemory

Y seguramente no quieres que sea responsable de inflar el presupuesto de hardware. ¿Puedes imaginar tener que explicar a tu equipo que 256 de memoria no es suficiente para ejecutar tu código en el servidor de producción?

Déjelo ayudarte.

No tiene que ser así. Si tienes una buena comprensión de el proceso de colección de garabatos y algunas mejores prácticas, puedes reducir dramatically de memoria de tu número.

¿Sonido de calidad

En los últimos 10 años he aprendido los secretos de la colección de garabatos en . NET, y en este curso te voy a compartir todos contigo.

En una serie de lecciones cortas, tomaré un vistazo a la el proceso de colección de garabatos. Te mostraré todos los problemas de asignación de memoria que puedes esperar cuando escribirá código C# de la que como boxe duplicación de corras, resizing, de colección y más. Te enseñaré estrategias rápidas y fáciles para resolver estos problemas.

Al final de este curso podrás dominar el coleccionde de garabatos.

¿Por qué deberías tomar este curso?

Debes tomar este curso si eres un desarrollador principiante o de C# intermedio y quieres llevar tus habilidades al siguiente nivel. La colección de garabatos y la gestión de memoria puede ser complicado, pero todas mis conferencias son muy fáciles de seguir, y explico todos temas con código de código claro y muchos diagramas instructivos. No tendrás problemas de continuación.

O tal vez trabajes en una sección crítica de código de un proyecto C# y debes asegurarte de que tu uso de memoria sea lo más eficiente de lo posible? Los consejos y trucos de este curso te ayudarán inmensos.

¿O tal que estés preparando para una entrevista de trabajo relacionada de C#? Este curso te dará una base excelente para responder cualquier pregunta que te pueda lanzar.

Conoce a tu profesor(a)

Teacher Profile Image

Mark Farragher

Microsoft Certified Trainer

Profesor(a)

Mark Farragher is a blogger, investor, serial entrepreneur, and the author of 11 successful Udemy courses. He has been a Founder and CTO, and has launched two startups in the Netherlands. Mark became a Microsoft Certified Trainer in 2005. Today he uses his extensive knowledge to help tech professionals with their leadership, communication, and technical skills.

Ver perfil completo

Calificaciones de la clase

¿Se cumplieron las expectativas?
    ¡Superadas!
  • 0%
  • 0%
  • Un poco
  • 0%
  • No realmente
  • 0%
Archivo de reseñas

En octubre de 2018, actualizamos nuestro sistema de reseñas para mejorar la forma en que recopilamos comentarios. A continuación, se muestran las reseñas escritas antes de esa actualización.

¿Por qué unirse a Skillshare?

Toma las galardonadas clases originales de Skillshare

Cada clase tiene lecciones cortas y proyectos prácticos

Tu membresía apoya a los profesores de Skillshare

Aprende desde cualquier lugar

Toma clases sobre la marcha con la aplicación Skillshare. Transmite o descarga para verlas en el avión, el metro o donde aprendas mejor.

Transcripciones

1. Optimizar el uso de la memoria en C#: Déjame hacerte una pregunta. ¿ Te gustaría convertirte en arquitecto de rendimiento C agudo? Vale, tengo que admitir, yo invento esa palabra. Pero en mi libro, un arquitecto de desempeño afilado C es un desarrollador senior que escribe abrigo afilado C de alto rendimiento . Por lo que un desarrollador senior que está agudamente consciente de las técnicas de optimización off coat sobre derechos, llamó a juegos para el análisis de datos para la adquisición de datos en tiempo real para todos estos entornos fríos donde la capa rápida es esencial, Así que ¿te gustaría convertirse en un arquitecto de rendimiento C agudo? ¿ Te gustaría unirte al club entonces? Este es el curso para ti. En este curso, te voy a enseñar una lengua fuera de los hacks de optimización. Miraremos la fundación fuera del punto net runtime. Por lo que te daré un curso acelerado en la pila en el montón. Cómo tipos de valor y tipos de referencia de tiendas en la pila, en el montón sobre cómo se mueven los datos entre la pila en el montón. Cuando estemos corriendo abrigo, estaremos mirando la optimización de la memoria. Entonces te enseñaré exactamente cómo funciona el recolector de basura en qué supuestos hace sobre los objetos, tamaño y el objeto. por vida sobre cómo puedes optimizar tu abrigo jugando en estas suposiciones, también vamos a ver algunos temas exóticos, como las horas finalizadas. Vamos a ver el patrón de disponer. ¿ Vamos a mirar varios pedazos de frío? Eso es que consumen demasiada memoria ahí, demasiado codicioso en la asignación de memoria, por lo que activan mucho el recolector de basura, y eso es otra vez ralentiza tu abrigo para que aprendas a escribir código que hace eficiente usar fuera de la memoria. Entonces este es un curso bastante grande. Contiene montones y montones de conferencias ahí cuestionarios para poner a prueba tus conocimientos. Se puede descargar el código fuente que he estado usando para benchmarking. Entonces, ¿te gustaría convertirte en una performance C Sharp Arquitectos? Entonces este es el curso para ti. Así que he creado este curso para nivel medio o senior ver desarrolladores de tiendas que quieran aprender a escribir rápido y eficiente capa afilada C para tener su carrera lista para temas geniales como desarrollos de juegos o adquisición de datos en tiempo real. Gracias por escuchar. Espero darle la bienvenida en el curso 2. Introducción a . Gestión de memoria de memoria de redes de: Echemos un vistazo a un típico programa de orilla del mar. El programa consistirá en clases que contengan campos y métodos. Un método típico comenzará con los objetos de valor de la canción que se declaran en inicializados. Después hay códigos para realizar algún tipo de acción. Y por último, el mensaje termina. asignan las variables en los métodos en la pila y cada vez que se ven las nuevas palabras clave en los objetos contra asignados en el montón. Pero aquí tienes una pregunta para ti. ¿ Qué es lo contrario? Off New, Qué palabras clave elimina en objetos Algunos lenguajes en realidad tienen unas palabras clave especiales llamadas delete, que es lo contrario de new on elimina explícitamente un objeto del montón, pero el punto net framework no tenerlo. Hay unas nuevas palabras clave, pero no hay cortesía. Elimina palabras clave. ¿ Cómo es eso posible? La respuesta es que existe un proceso especial llamado el recolector de basura que está borrando continuamente objetos que ya no son utilizados por tu abrigo. Quizás te estés preguntando por qué Microsoft optó por implementar una solución tan complicada. ¿ Por qué no simplemente agregar las palabras clave leads al lenguaje agudo C y evita la necesidad de un recolector de basura por completo? El motivo, agregaron el recolector de basura es muy sencillo en lenguajes que se basan en las palabras clave deletes . Los errores de programación más comunes se deben a la indagación. Jugo off New y Elise localizan un objeto con nuevo pero olvidando eliminarlo. Esto se denomina fuga de memoria, usando un objeto después de que ya se haya eliminado. Esto provocará la memoria, la corrupción y la eliminación de un objeto que no se ha asignado. Esto provoca un error en la memoria al agregar un recolector de basura al framework dot net, Microsoft ha eliminado por completo estos errores de programación. Es muy duro, no imposible. Mente ustedes dos crea la liga de memoria en C memoria de programa afilada o corrupta. Pero un recolector de basura también tiene desventajas porque la mitad fuera de la gestión de memoria ahora es totalmente automática. Los desarrolladores tienden a perder de vista las huellas de memoria fuera de sus códigos, y esto puede llevar a códigos que funcionan de la manera en que se supone que debe pero asigna miles de veces más memoria de la que realmente necesita. Te voy a mostrar cómo medir las asignaciones de memoria fuera de tus códigos. Te enseñaré exactamente cómo funciona el recolector de basura, y te compartiré varios trucos sencillos que te ayudarán a reducir drásticamente las huellas de memoria de tus propios códigos. 3. ¿Qué es la memoria?: pila de memoria era simplemente la pila es un bloque de memoria fuera que se utiliza para llamar a métodos y almacenar variables locales. Entonces déjame dibujar eso aquí en la pizarra. Dibujaré la pila como una columna vertical así. No, cuando empiezo a ejecutar algún código, esa pila inicialmente estará vacía. Pero cuando Mike Oates llama a los métodos esto sucede, los parámetros del método la dirección de retorno en todas las variables locales fuera de los métodos se ponen en la pila. Todo este bloque de datos se llama stack frame. Entonces, ¿qué pasa con la pila cuando mis códigos llaman a otro método desde dentro de este método, que esto lo mismo sucede otra vez? Los parámetros del método devuelven la dirección en variables locales fuera del nuevo método que pongo en la pila justo encima del marco de pila anterior. Entonces por eso se llama pila. La información se apila uno encima del otro. ¿ Qué pasa cuando mi culto encuentra declaración de retorno? Como probablemente sabrás, una declaración de retorno termina un método y regresa al código de llamada. Ahora en la pila. Esto es lo que pasa. elimina todo el marco de pila que correspondía al método, pero podría estar pensando ¿qué pasó con todas las variables locales que se almacenaron en el marco de la pila? Bueno, todos salen fuera de alcance, lo cual es sólo una forma elegante de decir que están destruidos. Entonces este es un hecho importante recordar el momento en que regresas fuera de los métodos todas tus variables locales fuera de que los métodos salgan de alcance y se destruyen. Si sigo con mi programa y también regreso de los primeros métodos, estamos de vuelta a donde empezamos con una pila vacía. Podría estar preguntándose qué sucede si un método llama a otros métodos, que causan otro método, que llama a otros métodos 1000 veces bien, la pila se llenaría rápidamente con marcos de pila hasta que haya espacio normal. Eventualmente, el stock estará completamente lleno en el marco neto DOT lanza pila. Excepción de desbordamiento. Si ves este mensaje de error, significa que tienes una secuencia infinita de llamadas de método off en algún lugar de tu código. Echemos un vistazo a algún código. Escribí un programa sencillo para dibujar un cuadrado en la pantalla. Ves que tengo un cajón métodos cuadrados que llama a un método de línea de cajón cuatro veces para dibujar los cuatro lados de un cuadrado. Pondré un descanso, puntos dentro del cajón, linear métodos y luego correr mi frío. Vigila esto. Ahora, en este punto de mi abrigo, esa pila se verá así. Mi primera llamada para dibujar cuadrado es aquí en la parte inferior de la pila con sus cuatro parámetros, dirección de retorno y variables locales. A continuación se presenta la llamada a una línea de cajón con nuevamente cuatro parámetros. Dirección de devolución el. En este caso, no hay variables locales porque la línea de cajón no tiene ninguna en estudio visual. Puedes echar un vistazo a esta pila abriendo la vista de pila de llamadas, que está aquí. Se puede ver la llamada en dibujar cuadrado y luego en una línea de cajón. Por lo que esta ventana te muestra qué marcos de pila se almacenan en la pila. Ahora mismo, como demostración final, permítanme mostrarles una pila. Excepción de desbordamiento. Déjame modificar mi abrigo así. He modificado mi abrigo. Entonces ahora dibuja una línea llama a una línea de cajón, que luego llama a una línea de cajón que llama a una línea de cajón. Obtienes la foto. Esta secuencia nunca terminará. Cuando ejecuto este programa, creará una secuencia infinita fuera de línea dibujada métodos llamándose a sí mismos Ejecutemos el programa y veamos qué sucede. Yo estoy ahí lo tienes. El stack es de tamaño limitado. Si tengo demasiados métodos llamando a todos los métodos, eventualmente la pila estará completamente llena en el marco de red DOT fluye la excepción de desbordamiento de pila . Entonces, ¿qué hemos aprendido? There'll Net Framework utiliza la pila para realizar un seguimiento de los médicos cada vez que se llama al mensaje todos sus parámetros de método. La dirección de retorno en todas las variables locales se colocan en la pila. Todo este bloque de memoria se llama marco de pila. Cuando regresa de un método, elimina el marco de la pila superior. Todas las variables locales salen de alcance en este punto, y destruí. Si tienes una secuencia infinita de métodos off, llamando a todos los métodos el stock se llenará por completo hasta que la ortiga arroje una pila. Excepción de desbordamiento 4. ¿Qué es la memoria de la heap heap: el otro tipo de memoria dentro de una computadora se llama memoria hit o simplemente el montón. Echemos un vistazo a la siguiente línea de código Ahora. Cada vez que las palabras clave nuevas aparecen en línea, estabas creando un objeto en el montón. Esta es una regla fundamental. Los objetos de redes interiores siempre se crean en el montón. Nunca van a la pila. Por lo que he hecho algunos cambios en mi programa de cajón cuadrado. Echemos un vistazo. Anteriormente, los métodos de línea de cajón esperaban. Cuatro introdujeron tus parámetros para dibujar una línea. Pero ahora tengo un cajón Métodos de polígono, que espera una matriz fuera de línea objetos en dibuja todo de una sola vez. El método draw square configura una matriz de líneas con cuatro objetos correspondientes a los cuatro lados de la plaza y luego llama Suelta todo un arma para dibujar todo en uno. Ve ahora Recuerda, en mi viejo programa cuadrado de cajón, puse unos puntos de ruptura dentro de los métodos de línea de cajón, y cuando ejecuté mis códigos, la pila se veía así. Pero ahora he hecho muchos cambios en el programa. Entonces, ¿cómo se va a ver ahora la pila en el montón? De acuerdo, entonces imagina que pongo unos puntos de descanso dentro del cajón. Métodos poligonales al ejecutar mi programa. El stack en el montón entonces se vería así. El parámetro frías líneas existe en la pila porque es un parámetro, pero lo inicializé con las nuevas palabras clave. Por lo que la matriz en sí se crea en el montón, por lo que la variable en esta pila se refiere a un objeto en el montón. También se puede ver que la matriz de líneas tiene cuatro elementos y que cada elemento se refiere a un objeto de línea en otro lugar del uno. Entonces, ¿ahora qué pasa cuando termine el método del polígono de dibujo? Ese marco de pila se retira en las luces, parámetro sale del alcance y se destruye. Pero aquí hay algo que podrías no haber esperado. El array en la línea Los objetos en el montón siguen existiendo ahora. Esta es una situación interesante. El parámetro leones está fuera de alcance, pero los objetos en el calor siguen ahí. Decimos que los objetos son de referenciados por la variable o parámetro que los refieren sin alcance. Los objetos de referencia siguen existiendo y no se destruyen de inmediato. Entonces aquí hay otro importante quitarle el marco dot net siempre pospondrá la limpieza de los objetos referenciados, y esto se debe a que limpiar el montón lleva mucho tiempo posponiéndolo el mayor tiempo posible . Tu código en realidad se ejecutará más rápido, pero eventualmente el marco tendrá que limpiar el montón o rápidamente nos quedaríamos sin memoria. Este proceso de limpieza se llama recolección de basura, y ocurre periódicamente en segundo plano cuando el marco inicia la recolección de basura . Identifica todos los objetos en el montón, que ya no son referenciados por ningún parámetro variable u objetos en tu pelaje en su de asigna cada uno de ellos. Entonces, ¿qué hemos aprendido? Cada vez que usaste las nuevas palabras clave en tus códigos, estás creando un objeto en el montón. La variable en sí puede vivir en la pila, pero se referirá a un objeto en el montón cuando los parámetros y las variables locales en la pila salgan del alcance. Los objetos correspondientes del montón no me destruyen. Siguen existiendo en un estado D referenciado. El marco net pospone la limpieza de los objetos referenciados en el montón por el mayor tiempo posible por razones de rendimiento. Pero eventualmente el marco iniciará un proceso llamado recolección de basura. A medida que de asigna todos los objetos referenciados en la cadera 5. ¿Cómo se almacenan tipos de valores en la memoria?: en la conferencia anterior, nos enteramos de esta pila en el montón ahora. Anteriormente, cuando hablé de esta pila, mostré algún código con el desorden de la línea del cajón. Es que usó cuatro parámetros enteros. Echemos un vistazo más de cerca a un entero en el marco de Dark Net. El tipo insurgente forma parte de una clase especial de tipos llamada tipos de valor, pero ¿cuál es el tipo de valor? El tipo de valor es un tipo de variable, donde el tipo y el valor de la variable se distorsionan juntos. Entonces si tengo una variable entera local con un valor de descuento 12 cientos y 34 ese tipo de entrevista en su valor se almacenará juntos así. Entonces echemos un vistazo a esta pila otra vez. Anteriormente, cuando hablé de esta pila, mencioné todo tipo de datos que se almacenan en el stock. Son parámetros de mensaje. La dirección de retorno de un mensaje suena variables locales. Entonces si tengo una variable local de tipo off en dibujado con el valor off 1234 se almacenaría en la pila así. Ya ves que el tipo y el valor almacenado juntos en la pila ahora tienen esto en mente porque en la próxima conferencia voy a hablar de tipos de referencia, que es el almacenamiento de manera diferente. Por lo que podría estar preguntándose qué tipos en el marco de red oscura son en realidad tipos de valor. Bueno, aquí hay una lista completa. Todos los tipos numéricos son tipos de valor, incluyendo todos fuera de los puntos flotantes enteros en tipos decimales. También, Booleano en operaciones y estructuras son tipos de valor. Cualquier otra cosa en el framework dot net se llama tipo de referencia, que voy a discutir en breve. Cuando las personas tratan de explicar la diferencia entre un tipo de valor y un tipo de referencia, a menudo se escucha la siguiente explicación. Un tipo de valor es un tipo que existe en la pila. No obstante, este es el valor de la habitación. Los tipos pueden existir tanto en la pila en el Sí, déjame demostrar. Anteriormente, cuando hablé del montón, mencioné qué tipo de datos se almacenan en el montón. ¿ Te acuerdas de lo que waas? Se waas todas las instancias de objetos creadas con unas nuevas palabras clave en C sharp. Entonces imagina que creo un objeto en el montón usando las nuevas palabras clave en mi abrigo en este objeto tiene uno entierro tu campo que contiene el valor 12 cientos 34. Ahora este entero se almacenará así. Entonces verá, sé que tienen el tipo de valor en el montón por lo que los tipos de valor pueden existir tanto en la pila en el día en los tipos de valor de propiedad definitoria no es donde se almacenan, sino que el valor se almacena junto con el tipo. Entonces déjame terminar mostrándote a importancia características adicionales o tipos de valor. Digamos que tengo un mensaje en mis códigos con dos variables A y B. Ambas se introducen. La variable A contiene el valor 1234 bajo la variable B es cero. Ahora lo que pasa cuando lo asigne será Echa un vistazo a esto. El valor de A se copia en be. Ahora esta es una característica importante de los tipos de valor sus signos por valor, lo que significa que su valor se copia sobre. Entonces ahora ¿qué pasa si comparo A y B? Se trata de dos variables diferentes que simplemente sucede que contienen el mismo valor. Entonces, ¿cómo interpretará bien el marco dot net la situación así? El marco considera que estas dos variables son iguales. Esta es la segunda característica importante fuera de los tipos de valor. comparan por valor, lo que significa que dos variables diferentes que sostienen el mismo valor se consideran iguales. Entonces, ¿qué hemos aprendido? Los tipos de valor almacenan su valor directamente junto con el valor de tipo. Los tipos pueden existir en la pila en y creer que los tipos de valor son un signo por valor, lo que significa que el valor se copia sobre el valor. Los tipos se comparan por valor. consideran iguales dos variables con el mismo valor. 6. ¿Cómo se almacenan tipos de referencia en la memoria?: En la conferencia anterior, hablé de los tipos de valor y mencioné brevemente a su contraparte el tipo de referencia. Pero, ¿qué es un tipo de referencia? Bueno, un tipo de referencia es un tipo de variable que se refiere a un valor almacenado en el montón. No antes. Cuando hablé del montón, mostré mi programa de dibujo cuadrado modificado que tenía un método de dibujar un polígono, si recuerdas con un parámetro de matriz de líneas, Así que dibujar polígono esperaba una matriz fuera de línea objetos. Echemos un vistazo más de cerca a esos objetos de línea solo para refrescar tu memoria. Aquí está mi abrigo otra vez. Aquí se puede ver la definición fuera de la clase de línea. Se trata de un simple contenedor de datos con dos conjuntos de coordenadas. Entonces imagina que tengo un mensaje con una variable local fuera de línea de tipo. ¿ Cómo se vería eso? Un recuerdo bien, así. Se puede ver que la variable en sí está en la pila, pero se refiere a una línea Objetos en el pero ¿también puede existir una variable de tipo de referencia en el montón? Sí, seguro. Todo lo que necesito hacer es crear un objeto en el montón. El uso de las nuevas palabras clave giró a la mitad que los objetos tienen un campo fuera de línea de tipo. Ahora el recuerdo entonces se verá así. ve que ahora tienen una variable de tipo de referencia en el montón, y se refiere a una línea de objetos, que también se almacena en el qué en otro lugar, por lo que para resumir. Los tipos de referencia pueden existir en la pila el día en el montón, igual que los tipos de valor, pero siempre se referirán a un valor en el montón. Déjame terminar mostrándote a importancia características adicionales fuera de los tipos de referencia. Digamos que tengo un mensaje en mis códigos con dos variables A y B. Ambas variables son líneas. La variable A se refiere a una instancia de línea en el montón bajo la variable B se dice que sabe. Ahora qué pasa cuando le asigne a a B. Revisen ustedes mismos La referencia off A se copia en ser. Esta es una característica importante fuera de los tipos de referencia. Se asignan por referencia, lo que significa que la referencia se copia sobre. Terminas con dos variables, refiriéndose a la misma instancia de objeto en la cadera. Entonces ahora ¿qué pasa cuando comparo A y B? Se trata de dos variables diferentes que hacen referencia a los mismos objetos en el montón. ¿ Cómo interpretará bien esta situación el marco de punto net. El marco considera que estas dos variables son iguales. Pero espera, ¿qué pasa con este escenario para hacer referencia a variables de tipo que apuntan a dos objetos separados en el montón. Pero ambos objetos contenían datos idénticos. ¿ Cómo interpretará el marco dot net esta situación Sonidos bebedor. El marco considera que estas dos variables no son iguales, por lo que esta es otra característica importante fuera de los tipos de referencia. comparan por referencia, lo que significa que dos variables diferentes referidas a los mismos objetos se consideran iguales. Pero dos variables diferentes, refiriéndose a dos objetos separados pero idénticos, se consideran no iguales. Entonces, ¿qué hemos aprendido? Los tiempos de referencia se pueden percibir al almacén de tipos de referencia sin valor, una referencia a su valor, y este valor siempre se almacena en la referencia. Los tipos pueden existir en esta pila el día en el, pero su valor siempre se almacena en la referencia. Los tipos se asignan por referencia, lo que significa que la referencia se copia sobre la referencia. Los tipos se comparan por referencia. consideran iguales dos variables que hacen referencia a los mismos objetos, y dos variables que se refieren a objetos separados pero idénticos se consideran no iguales 7. ¿Qué es el boxeado y boxing: en esta conferencia, les voy a mostrar un misterio. Echa un vistazo a este abrigo. Este es un programa muy sencillo. Empecé con la variable A que contiene el Valor 1234. Entonces declaro una segunda variable. Estar fuera de tipo objetos y asigno a a B en C sharp todas las veces heredan de los objetos, incluidos los enteros, por lo que puedes poner básicamente todo en una variable de tipo de objeto. Pero espera Los jurados Inter son tipos de valor, y los objetos son tipos referenciados. Entonces en la memoria, mis variables se almacenan así. Aquí está mi variable entera A con su valor off 1234. Y aquí está mi variable de objeto B. Pero B es un tipo de referencia, y hemos aprendido en la conferencia anterior que los tipos de referencia siempre se refieren a un valor en el montón aquí. Esto no es posible porque A es una variable local, por lo que existe en la pila el día. Es un tipo de valor, por lo que su valor también se inicia en la pila. Simplemente no hay forma de que B se refiera a a a porque ambas variables recitan en diferentes tipos de memoria. Un tipo de referencia nunca puede referirse a la memoria de pila por lo que este programa nunca puede funcionar. ¿ Verdad? Bueno, éste sí. Vuelvo a diciembre en el estudio Andi, ejecuto el programa. Aquí vamos. Enfriar. En realidad funciona. Pero, ¿cómo es eso posible? Ahora, esto es raro. En base a lo que aprendimos en conferencias anteriores, este programa no debería funcionar. Pero aún así lo hace. ¿ Cómo es eso posible averiguarlo? Permítanme de compilar este programa en lenguaje intermedio. Al examinar las instrucciones del lenguaje intermedio, podríamos encontrar una pista. Y aquí está. Mira esto. Instrucción de idiomas intermedios llamados libros. ¿ Se puede adivinar qué hace aquí? Dibujaré en la pizarra. Aquí están los diseños de memoria con las dos variables A y B. Ahora la instrucción de libros hace esto. Entonces para hacer que el programa funcione, el marco Net realmente copia el valor del entrevistador de la pila, luego gravar el valor en los objetos en él coloca thes objetos en el para que la variable B pueda entonces referirse a su. Todo este proceso se llama boxeo. El boxeo ocurre cada vez detrás de bambalinas. Cuando tienes un parámetro variable campos o objetos de tiempo fuera de propiedad y asignas un tipo de valor a su boxeo es agradable porque tipo de desdibuja la línea entre tiempos de valor y tipos de referencia. Pero el boxeo también puede ser un dolor porque introduce gastos generales adicionales en tu código. Ahora quizá te estés preguntando si hay un proceso correspondiente llamado unboxing. Sí, lo hay. Aquí está mi programa otra vez. Echemos un vistazo a la línea final. Declaro una variable, veo fuera de tipo entero y asigno el valor de los objetos a su usando un tipecast. Otro poco de magia. En realidad, porque ver, existe en la pila y ser los objetos se refiere a un objeto en el en el lenguaje intermedio . Aquí está la instrucción correspondiente. Se llama Unbox. Déjame volver a la pizarra on Dibuja el proceso de unboxing. Empezamos desde la situación de caja con el entero en el Ahora. Entonces esto sucede. Desboxeo desempaqueta el entero en el calor y copia el valor de nuevo en la variable Ver en la pila. desboxeo ocurre cada vez detrás de bambalinas cuando tienes un valor de objeto y lo arrojas a un tipo de valor. Entonces, ¿qué hemos aprendido? boxeo es el proceso fuera tomando tipos de valor en la pila, empacándolos en objetos como colocando estos objetos en el montón. El boxeo ocurre siempre que asignas tipo de valor a un parámetro variable de campo o propiedad fuera de objetos de tipo. Unboxing es el proceso inverso. Los objetos fuera del montón se desempaquetan en el valor. tipos dentro se copian de nuevo a la pila. Aprender. El boxeo ocurre siempre que no tienes valor objeto, y lo arrojas dedo del pie un tipo de valor. 8. Cómo funciona de la colección de garabatos. ¿NET? En: en la segunda conferencia fuera de este curso, la de memoria de montón en la sección de fundamentos, vimos lo que sucede cuando las variables de tipo de referencia salen de alcance. Las variables que existe en la pila se destruyen en los objetos correspondientes en el montón. R D Los objetos referenciados de referenciados siguen existiendo y no se destruyen de inmediato. Mencioné brevemente que un proceso separado llamado el recolector de basura limpia periódicamente estos objetos. Entonces en esta conferencia, vamos a echar un vistazo más de cerca al recolector de basura. ¿ Qué es el recolector de basura y cómo funciona? Empecemos con un programa muy sencillo. Este programa sólo tiene un mensaje con un array de objetos variables locales. El array se inicializa con cinco objetos en. Estos cinco objetos también residen en el montón adyacente a la matriz misma. Ahora, para hacer las cosas más interesantes, eliminemos los elementos de matriz dos y tres sentados. Ahí array elementos para conocer los objetos correspondientes. Los números dos y tres siguen existiendo en el calor, pero ahora están de referenciados. No hay referencias a estos objetos desde ninguna parte del abrigo. ¿ Qué sucede cuando el recolector de basura patea en el recolector de basura de la red de la puerta es un recolector de marca y barrido, lo que significa que hay dos etapas distintas de la basura recolectando una etapa de marca en una etapa de barrido durante la etapa de marca. El recolector de basura marca todos los objetos de vida en el calor, por lo que en este ejemplo, esa sería la matriz en sí. En los objetos 01 sobre cuatro. los objetos dos y tres omitenlos objetos dos y tresporque no hay referencias a estos objetos desde cualquier parte del abrigo. A continuación viene la etapa de barrido todos los objetos que tienen nots y siendo marcados en la etapa anterior referencia R D. Y en esta etapa están de asignados desde el montón. Entonces en este ejemplo, los objetos dos y tres no tienen bean marcado en, por lo que son los asignados en. Esto deja una especie de agujero en el montón. El recolector de basura de red para perros realiza un paso adicional después del barrido, que se llama compactos. En la etapa compacta, se eliminan todos los agujeros del montón, por lo que en este ejemplo, el objeto cuatro se mueve hacia arriba para llenar el agujero. Ese objeto para dejar atrás la marca y barrer recolector de basura es muy bueno para localizar todos y cada uno de los objetos referenciados de trato en el montón en quitarlo, pero también tiene un gran inconveniente. Es muy lento. Durante la etapa de marca, el recolector de basura tiene que inspeccionar cada objeto en la memoria para determinar si es vida o referenciado. Si hay muchos miles de objetos fuera en el montón, su programa realmente se congelará por un tiempo ya que el recolector de basura está inspeccionando todos y cada uno de los objetos. El proceso también es muy ineficiente porque los objetos de larga vida en el montón son revisados y rerevisados, soportando cada ciclo porque podrían ser d referenciados en cualquier momento. Por lo que un objeto vivo muy largo podría ser revisado cientos de veces si aún está vivo. A la solución a este problema se le llama recolector de basura generacional. El recolector de basura de redes de puntos es generacional on. Tiene tres generaciones, que se pueden visualizar como tres montones separados. Todas las nuevas asignaciones van al primer montón generacional llamado Generación Cero. Entonces si volvemos a visitar el programa de prueba con la matriz de cinco elementos con los elementos dos y tres resuelven no, entonces el diseño de memoria se vería así. Todo es igual que antes, pero ahora todos los objetos están residiendo en generación cero generaciones uno viene a nuestro anti. El primer ciclo de recolección hace una marca y barrido y todos los objetos que sobrevivieron al barrido se mueven a Generación uno. Entonces, después de un ciclo, el diseño de la memoria se parece a esta matriz Z en y Objetos 01 y cuatro han sobrevivido al barrido y ahora están en la generación uno. Ahora imagina que el programa continúa en este punto. Tía pone un nuevo objeto cinco en elementos array a todas las nuevas asignaciones. Entra en una generación cero para que los diseños de memoria se vean así. Como ven, esta es una situación interesante. El array reside en la Generación uno, pero sus elementos están en generaciones cero y uno. Esto es perfectamente válido. Ahora el recolector de basura vuelve a patear por un segundo ciclo. Todos los objetos de generación uno se mueven a generación a en el nuevo objeto en Generación cero se mueve a generación uno. Si el programa continúa y pone un nuevo objeto seis en un raro elementos tres, volvería a entrar en la generación cero. Ahora tenemos una matriz en generación para referirnos a objetos en la generación 01 tiene que volver a prueba de la violencia, por lo que podría estar preguntándose en este punto por qué está sucediendo todo esto. ¿ Por qué han tenido estas tres generaciones? Bueno, la respuesta es muy simple. Las generaciones ayudan a limitar el número fuera de los objetos en la generación cero. Cada ciclo de colección borra por completo la generación cero de todos los objetos. Por lo que en el siguiente ciclo, el recolector de basura sólo tiene que inspeccionar nuevos objetos que se crearon después del último ciclo. Por supuesto, el problema no va a desaparecer. El recolector de basura simplemente movió los objetos a otro lugar. Pero aquí están las generaciones clave, una en cuanto a se recogen con muy poca frecuencia. El recolector de basura asume que cualquier cosa que llegue a generación debe ser un objeto de larga vida que no necesita ser revisado muy a menudo, por lo que esto resuelve dos problemas. En primer lugar, reduce el número de objetos fuera en la generación cero, por lo que el recolector de basura tiene menos trabajo que hacer. Segundo, objetos de larga vida que sobreviven en generación a no me revisan muy a menudo, que es exactamente lo que queremos. El recolector de basura generacional es un hermoso algoritmos de alto rendimiento, pero tiene un inconveniente importante. Es ineficiente ya que el procesamiento de objetos grandes, de larga vida consideran que un objeto grande y de larga duración se asignarán en generación cero. Sobrevive al primer ciclo, y el calor se compacta, lo que potencialmente mueve el objeto en la memoria. Después pasa a la generación uno. Se compacta en movimientos a generación a todos. En total, se trata de dos tez y dos movimientos, Así que un total off cuatro operaciones de copia de memoria para un solo objeto antes de que llegue en generación a en el recolector de basura lo ignora por un tiempo. Si el objeto es muy grande, estas cuatro operaciones de copia por objeto pueden introducir unos gastos generales de rendimiento de significación. Por lo que esta solución a este problema es tener dos montones separados, uno para objetos pequeños en uno. Para objetos grandes, el diseño se parece algo a esta indignación que son demasiado caderas. El pequeño montón de objetos, que funciona con las tres generaciones que discutimos antes de manos la gran cultura conservan. Lo especial del montón de objetos grande es que no utiliza generaciones. De hecho, sólo tiene una sola generación, cual se sincroniza con la generación a off del pequeño objetivo. Por lo que cuando el recolector de basura eran procesos generación para salir de lo pequeño de tu equipo, también corre a través de todo el objetivo grande. Otro dato interesante sobre el gran objetivo es que no compacte el durante el ciclo de barrido. Simplemente fusiona bloques de memoria libres juntos, pero no hace ninguna compactación para optimizar la cantidad total de espacio libre. Te podría estar preguntando ¿qué determina si un objeto es pequeño o grande? Bueno, el umbral de tamaño está en 85 kilobytes. Cualquier objeto a 85 kilobytes eran más grandes va directamente al montón de objetos grande. Cualquier objeto menor que este límite entra en el montón de objetos pequeños. Tener estos dos montones separados resuelve el problema de objetos grandes y de larga vida. Ya no necesitan copiarse cuatro veces antes de que terminen en generación a, sino que van directamente al gran montón de objetos, que sólo se procesa en generación a y nunca se compacta. Y ahí lo tienes. El recolector de basura de redes para perros es una calidad de basura generacional o que utiliza un ciclo compacto de barrido marca , y tiene montones separados para objetos grandes y objetos pequeños. Si lo piensas, el recolector de basura de punto Net hace algunas suposiciones muy específicas sobre objetos y vidas. En primer lugar, asume que los objetos serán o bien hojas cortas o de larga vida. Todos los objetos de elevación corta deben asignarse, utilizarse y descartarse en un solo ciclo de recolección. Cualquier objeto que se desliza a través de las grietas por así decirlo, queda atrapado en la generación uno en el siguiente ciclo, por lo que cualquier objeto que sobreviva a los ciclos de recolección termina en generación a y debe ser un objeto vivo largo. Además, cualquier objeto de más de 85 kilobytes de tamaño siempre se considera un objeto vivo de larga duración. Al mirar la frecuencia de recolección fuera de las diversas generaciones, es evidente que el recolector de basura asume que la abrumadora mayoría fuera de los objetos será de corta duración. Por lo que puedo resumir mi consejo de optimización de memoria en una sola frase. No ir en contra de estos supuestos. Entonces, ¿qué hemos aprendido? El recolector de basura utiliza un barrido de marca y un ciclo compacto. El recolector de basura tiene dos montones separados para objetos grandes y pequeños. El gran montón de objetos en el pequeño objetivo. El objeto pequeño que utiliza tres generaciones. Todos los objetos nuevos se asignan en generación cero sobre el avance hacia la generación al objeto grande. Tiene una sola generación la cual se procesa junto con la generación a off del pequeño objetivo. Además, el montón de objetos grande no compacta el montón. Para optimizar la memoria libre, el recolector de basura hace dos suposiciones importantes sobre los tamaños de objeto y las vidas de un 90% de descuento en todos los objetos menores de 85 kilobytes deben ser vidas cortas a todos los objetos mayores de 85 kilobytes deben ser de larga vida y 9. Consejo #1: optimiza tu código para la colección de garabatos: en esta conferencia vamos a ver varias optimización es hacer que el recolector de basura funcione lo más rápido posible. Pero primero, recapitulemos lo que aprendimos sobre el recolector de basura. En la conferencia anterior, el recolector de basura de redes DOT utiliza un barrido de marca en ciclo compacto para limpiar D objetos referenciados del calor. Es uso es separar montones para objetos grandes y pequeños. El gran o embarcadero de este pequeño objeto acumula los objetos pequeños que utiliza. Tres generaciones, todos los objetos nuevos se asignan en generación cero manos. avance hacia generación a generación cero se recoge con mucha frecuencia. Generaciones una mano demasiado menos así que Generaciones ayudaron a limitar el número de objetos fuera en la generación cero. Cada ciclo de recolección borra completamente la generación cero de todos los objetos en el siguiente ciclo , el recolector de basura sólo tiene que inspeccionar los nuevos objetos que se crearon después del último ciclo. La primera optimización de rendimiento basada en memoria que vamos a ver es simplemente limitar el número de objetos que creamos en generación cero. Cuanto menos se creen objetos, menos trabajo tiene que hacer el recolector de basura para limpiar el montón. Existen dos estrategias que puedes seguir para limitar el número de objetos fuera de la cadera. El primero es asegurarte de que tus códigos no creen ningún objeto redundante en ningún lugar segundo para asignar, usar y descartar tus objetos lo más rápido posible para que ya sean los asignados por el recolector de basura en el siguiente ciclo. Si esperas demasiado tiempo entre asignar, usar en descartar tus objetos, corres el riesgo del final en generaciones una o dos. Por lo que para objetos de corta vida, quieres que tus abrigos estén lo más ajustados posible. Echemos un vistazo a algunos ejemplos. Aquí hay un fragmento de código que bucle 10,000 veces en construye una cadena, usando un constructor de cadenas con un método frío al apéndice. ¿ Se puede ver el problema con este abrigo? Te daré 10 segundos para que pienses. Aquí está la solución. El problema es con la concatenación de cadenas Dentro del mensaje anexa, recordarás que las cadenas son inmutables, y por lo tanto el mensaje de dos cadenas en la adición ambos crea objetos de cadena extra en el montón para cada bucle it oration. El frío en la parte inferior evita este problema por montaje, llamando a upend dos veces. La diferencia es de 40,000 menos objetos de cadena en el montón. Esa es una gran mejora. Entonces aquí hay otro ejemplo. A ver si puedes avisar el problema. Te volveré a dar 10 segundos. Y aquí está la solución. Si almacena enteros en una lista de matrices, los enteros se boxean en. El las listas genéricas evita este problema mediante el uso en matriz de enteros internos en lugar de una matriz de objetos, una simple modificación a los códigos que da como resultado 20 mil menos caja en sus objetos en el bien. Un ejemplo más. un objeto pequeño inicializaun objeto pequeñoy estático, luego se ejecuta un montón de otro culto primero en. Por último, el objeto realmente se acostumbra. ¿ Qué tiene de malo esta imagen? Te daré 10 segundos, y aquí está la respuesta. El objeto es pequeño, pero la brecha entre la asignación y el uso es muy grande, por lo que hay una gran probabilidad de que los objetos terminen en la generación uno o dos antes de que se acostumbre. El código en la parte inferior evita este problema. Mi primero, hacer que los objetos no estáticos hay localizando es justo antes de su uso y finalmente establecer la referencia de objeto para saber justo después de su uso para señalar al recolector de basura que se hicieron sobre que los objetos están listos para ser recogido. Si no te gusta tener asignaciones en todos tus códigos, también puedes envolver los códigos inferiores en el mensaje, igual que el objeto de referencia. Salga del alcance cuando salga de los métodos. Esa es mi solución favorita. La siguiente optimización que puedes realizar es afinar la vida útil de tus objetos. El recolector de basura asume que eso es casi todo. Los objetos pequeños serán de corta duración, y todos los objetos grandes serán de elevación larga, por lo que debemos evitar lo contrario. Pequeño, elevación larga, todo tex o grandes en breve sujetos. Es instructivo ver estas combinaciones en una gráfica. Si trazar la vida útil del objeto horizontalmente en el tamaño del objeto verticalmente, obtengo los siguientes gráficos. Los cuadrantes inferior izquierdo y superior derecho están donde quieres estar. Estas combinaciones fuera de objetos, tamaños y tiempos de vida coinciden exactamente con los supuestos fuera del recolector de basura. Los cuadrantes superior izquierda e inferior derecha están en desacuerdo con los supuestos fuera del recolector de basura . Si tu código contiene muchos objetos de estos cuadrantes, estás trabajando efectivamente contra las suposiciones del recolector de basura de que el rendimiento de tu abrigo probablemente sufrirá como resultado de ello. Entonces, ¿qué podemos hacer para meternos en los cuadrantes correctos? Empecemos con los objetos. Vida útil para re factor grandes, objetos de elevación cortos que necesitamos para aumentar el objeto. por vida. Existe una estrategia muy simple para esto, que se llama pool de objetos. La idea es que en cambio muchas veces descartar y objetos y asignar y nuevos objetos. En su lugar, reutiliza los objetos existentes. Debido a que los mismos objetos se están utilizando una y otra vez, efectivamente se convierte en un objeto vivo largo. Esta es una estrategia muy popular para optimizar el montón de objetos grandes. Entonces, veamos un ejemplo. Aquí hay un fragmento de códigos que asigna una lista de matriz grande y luego la usa dos veces, descartando en reasignar la lista entre usos. ¿ Cómo mejorarías este abrigo? Te daré 10 segundos para pensarlo, y aquí está la solución. En cambio, fuera descartar al reasignar la lista, en su lugar la limpias con una llamada al mensaje claro encendido, luego reutilizas la lista para el segundo método, llama en el nuevo abrigo. El mismo objeto de lista de matriz se utiliza una y otra vez. Se incrementa su vida útil en los objetos efectivamente se convierte en una larga vida. Este cambio mejora el rendimiento en reduce la probabilidad de que el montón de objetos grande se fragmente. Ahora veamos el problema inverso. Tenemos un pequeño, largo levantar objetos que debemos refractar o en unos objetos de corta vida. ¿ Cómo funcionaría eso? Aquí tienes un ejemplo. Este abrigo llena una lista de array con 10,000 pares de objetos, libras cada cabello contiene dos enteros. Entonces, ¿qué tiene de malo este código? Te daré 10 segundos para pensarlo. El problema es que la lista de matriz es un objeto grande, por lo que pasa al montón de objetos grande y se asume que es de larga vida. Pero la lista está llena de pequeños objetos de par 10,000 de ellos. Todos estos objetos van hasta que el objeto pequeño se acumula en la generación cero, porque la lista de matriz está manteniendo un dedo del pie de referencia cada aisam, todos estos padres nunca d referencia, y eventualmente todos se moverán a una generación. A la solución es utilizar otra estrategia popular, re factoring en su lugar, off tener una lista con dos enteros en cada elemento de lista, partimos la lista de partes en dos inter genera separados porque un entero es un valor tipo, se almacenará con la matriz. Por lo que ahora sólo tenemos dos aumento más grande en el montón de objetos grandes y absolutamente nada en la generación cero. Problema resuelto. El tercer optimización que puedes realizar es encontrar junio del tamaño de tus objetos. El recolector de basura asume que casi todos los objetos lentos serán de corta duración. Todos los objetos grandes serán de larga vida. Entonces si tenemos los opuestos en nuestros códigos, elevación larga escolar o objetos grandes cortos izquierdos, necesitamos refraccionar el tamaño de estos objetos para que vuelvan a entrar en la carga correcta. CONFORMIDAD. Empecemos con un gran objeto corto izquierdo para reducir el tamaño de este objeto. Hay dos estrategias. Uno dividió el objeto de partes en sub objetos, cada uno menor que 85 kilobytes o dos. Reducir la huella de memoria fuera del objeto. Aquí te dejamos un ejemplo fuera de la segunda estrategia. Un bucle llena el buffer con 32 miles, pero es ¿se puede ver qué le pasa a sus códigos? Te daré 10 segundos. Y aquí está la respuesta. El bucle llena el buffer con picaduras, pero el buffer se define como una matriz fuera de enteros. El buffer tiene capacidad para 32 mil artículos. Andi. Dado que un entero tiene cuatro bytes de tamaño, esto suma 100 28 miles de picaduras. Esto está por encima del umbral de objeto grande, y por lo tanto este buffer va directamente hasta que el montón de objetos grandes en se recoge en generación a la solución es re factorizar el buffer ya que muerde buffer. Ahora las huellas de memoria fuera del búfer son exactamente 32,000 bytes, que es más pequeño que los umbrales de objetos grandes. Y así se pone tiendas en el pequeño objetivo en generación cero, igual que nosotros waas. Ahora, veamos el problema inverso. Tenemos un objeto pequeño, de larga vida que debemos re factorizar en un objeto de elevación grande, largo. ¿ Cómo funcionaría eso? La solución es, bien sea y grande la huella de memoria fuera del objeto o fusionar varios objetos juntos para crear unos objetos más grandes que puedan ir en el montón de objetos grande. Entonces aquí está el ejemplo final de esta conferencia, este abrigo declara una lista de matriz estática en. Entonces, en algún lugar a mitad de camino, el programa empieza a usarlo. ¿ Qué tiene de malo este código? Te daré 10 segundos. Aquí está la respuesta. Está claro que se pretende que el objeto sea un objeto vivo de larga duración porque se declara estático . Si también sabemos que la lista eventualmente contendrá al menos 85 kilobytes de días, ¿eh? Entonces es mejor inicializar la lista a este tamaño. Esto asegura que la lista vaya directamente al montón de proyectos grande porque si no inicializas la lista, obtiene la capacidad predeterminada, que de la parte superior de mi cabeza es de 16 kilobytes. Entonces la lista va hasta que el pequeño montón de objetos en generación cero y eventualmente se mueve a generación a después potencialmente, habiendo sufrido cuatro operaciones de copia de memoria inicializando la lista al tamaño correcto de inmediato, te una voz, la migración de generación cero a generación a enteramente. Podría parecer extraño que puedas optimizar el abrigo haciendo más grandes objetos, pero eso es exactamente lo que estamos haciendo aquí. Y a veces realmente funciona. Entonces, ¿qué hemos aprendido para optimizar tu código de tal manera que el recolector de basura se ejecute rápido posible? Es necesario seguir estas estrategias primero, limitar el número de objetos fuera que crea segundos asigna uso en descartes objetos pequeños lo más rápido posible. Tercer's reutilizar todos los grandes proyectos. Se quiere trabajar con el recolector de basura y no en contra. Y así debes asegurarte de que todos los objetos de tus códigos sean pequeños y de corta duración. Cuatro vidas grandes y largas. Entonces si sucede que tienes objetos que son o bien elevadores grandes y cortos o pequeños en elevación larga , es posible que quieras volver a factorarlos para sujetos grandes en breve. Puede aumentar la vida útil o disminuir el tamaño del objeto y cuatro objetos pequeños y largos de elevación. Puede disminuir el signo de vida o aumentar el tamaño. Todos estos cambios beneficiarán el rendimiento de tu abrigo. 10. Consejo #2: evitar los finalizadores de clase: en esta conferencia, me gustaría hablar de Finalizar er's. Un izer final es un classmethod que se ejecuta automáticamente justo antes en los objetos se limpia por el recolector de basura. Finalizar. ER también se llaman a veces estructuras D para significar que son los opuestos fuera los constructores, mientras que un constructo er inicializar es y los objetos en lo prepara para el primer uso una estructura D hace la tía opuesta limpia cualquier persistente. El recurso es para que los objetos se puedan descartar. Finalizar er's son muy útiles si asignas recurso escaso está en tu objeto. Por ejemplo, en el manejo de archivos del sistema operativo, solo hay un número limitado de manejadores de archivos disponibles, por lo que es muy importante liberar el mango cuando haya terminado con el objeto. Claro, podrías implementar un mensaje de cierre. Pero ¿y si alguien se olvida de llamarlo? Es más seguro liberar el manejador de archivo en un Isar final. Entonces es que se garantiza que se suelte cuando el objeto se limpia en abrigo que se vería algo como esto. Se puede ver la declaración de clase en la parte superior en el constructor. Ese inicializar es el objeto. Andi, debajo del constructor hay un método de aspecto extraño que comienza con un personaje tilde. Este es el Isar final on. Será llamado por el recolector de basura justo antes de que se limpie el objeto. En conferencias anteriores, les mostré cómo funciona el recolector de basura. Se podría recordar este ligero con las tres generaciones en la matriz fuera de los objetos moviéndose a través de ellos. Echemos otro vistazo al proceso de recolección. En este momento, supongamos que cada elemento de matriz tiene un finalizado. Entonces si volvemos a visitar ese escenario con los elementos dos y tres establecidos para conocer, entonces los diseños de memoria se verían así. Todo es igual que antes, con todos los objetos residiendo en generación cero generaciones. Uno en están vacíos. El primer ciclo de recolección hace una marca en barrido en todos los objetos que sobreviven esta semana se trasladan a la Generación uno. Entonces después del primer ciclo, el diseño de la memoria se ve así. El array sobre los objetos 01 y cuatro han sobrevivido a este dulce y ahora están en la generación uno. Pero aquí está el cambio. Los objetos dos y tres no se limpian. No pueden ser porque han finalizado Er's, que aún necesita ser ejecutada, Así que el recolector de basura también mueve objetos dos y tres a la generación uno y los agrega a una finalización especial que después del ciclo de recolección completa una amenaza separada llamada Finalization Threads se despierta. Andi va a trabajar, ejecutando el er finalize fuera de todos los objetos en la finalización Que Echemos un vistazo a algún abrigo real que he escrito. Hice un pequeño programa que asigna objetos con Finalize er's para que podamos ver ido. Es así como sus llamadas echan un vistazo a este código. Aquí está el mensaje principal del programa que asigna repetidamente objetos en un bucle. Cada objeto obtiene un número de índice único para que podamos rastrearlos individualmente. Cuando presiono una tecla, termina el bucle. La aplicación escribe un texto en la consola que está terminando en el programa principal. Hilos termina, y aquí está la declaración de clase fuera de la clase Mi objeto. Es una clase muy simple con solo un solo entierro tus campos Onda, un constructor para inicializar el entero. El constructor tiene aquí una línea extra que derechos a la consola en la que la generación de recolección de basura , los objetos resorts. Andi aquí está la estructura Clase D o finalizar er. Duerme durante 500 milisegundos y luego escribe la generación actual en el número de índice en la consola. De acuerdo, vamos a ejecutar el programa. Aquí vamos. Todo parece estar funcionando mucho fuera de los objetos que se construyen en la generación cero, tal y como esperaríamos. Ahora voy a presionar una tecla. Se puede ver que se llama a Finalize ER una vez cada 500 milisegundos. La principal amenaza del programa ha terminado, pero la finalización Q sigue llena de objetos, por lo que los hilos de finalización siguen adelante, finaliza objetos uno por uno hasta que finalmente termina el proceso. Pero mira esto primero. Los números de índice están en orden aleatorio, por lo que sabemos que el recolector de basura está procesando los er Finalize en segundos de orden aleatorio . Todos los objetos están finalizados en Generación uno y espadas. Después de presionar una tecla, la aplicación finaliza ocho objetos en, luego termina lo que sucede con los millones de otros objetos. Entonces ya deberías tener una corazonada de que finalizar los er's son cosas complicadas. Sentido son fáciles de equivocarse. Vamos a recorrer las observaciones. Uno por uno, empezaré con el borde aleatorio fuera de los números de índice. Por lo que hemos visto que el recolector de basura procesa objetos en un orden completamente aleatorio. Esto significa que tus códigos finales de Isar no pueden hacer suposiciones sobre que otros objetos siguen vivos. Cualquier referencia que puedan contener tus objetos ya podría estar finalizada. Esto lleva a una regla muy importante Al escribir finalizar ER un Isar final sólo debe procesar sus propios objetos y nunca otros objetos de referencia. Aquí un buen ejemplo. Echa un vistazo a este código. Si el escritor Stream se finaliza primero, todo está bien. El dato se vacía al disco encendido. Después se cierra el expediente. Pero si los objetos se finalizan al revés redondea, entonces el escritor de stream intentará escribir datos en un archivo cerrado en la aplicación se aplastará. Por lo que este abrigo en realidad se estrellaría un 50% de descuento en el tiempo, dependiendo del pedido fuera de los er de finalización. Pero en realidad, esto no sucede. Microsoft vio venir este problema y decidió no implementar un er de finalización en la clase de escritor de stream . Entonces si olvidas cerrar el elevador de flujo, tu código sabrá que se bloquea. Pero la cadena tampoco se escribirá en disco. Perderás datos, por lo que en resumen, no puedes hacer ninguna suposición sobre el orden en el que se finalizarán tus objetos y nunca debes intentar usar un objeto referenciado en tu Isar final porque ya podría se finalice. Muy bien, Lo segundo que observamos desde el código es que todos los objetos están finalizados en la Generación uno. Esto tiene perfecto sentido cuando lo piensas. El recolector de basura marca todos los objetos de la vida en generación cero. Andi los mueve a la generación uno. Todos los objetos D referenciados con Finalize ER's también se mueven a Generación uno para su custodia y lo agregan a la finalización que Los hilos de finalización procesan los objetos uno por uno, llamando a cada ulalize er on, eliminando los objetos de la cola. Después de cada finalizar ER ha corrido. Finalmente se limpian los objetos en el siguiente ciclo de recolección. Hemos visto en una conferencia anterior que el recolector de basura está optimizado para objetos pequeños, de corta vida encendidos con elevación corta. Quiero decir en objeto, cuya vida entera encaja en Generación cero. Entonces aquí hay una gran desventaja fuera de los objetos con Finalize er's. Su vida útil siempre abarcará al menos la generación cero. En el primer día, se necesitan al menos dos ciclos de recolección para que finalmente se limpien los objetos, con el Isar final ejecutándose entre colecciones. Y si hay muchos objetos en la cola. Incluso podría tomar hasta generación para antes de que el objeto finalmente se finalice. Si tienes muchos objetos de elevación pequeños y cortos con Finalize ER, realidad vas contra los recolectores de basura o porque estás extendiendo su vida útil a la generación uno y con ello duplicando el número de operaciones de copia de memoria necesario para marcar barrido y compacto profundo. Otra desventaja fuera finalizar er's es que cualquier objeto al que refieras también terminará en la generación uno. Entonces si implementa una clase de cobranza con el Isar final, esto significa que la colección más todos los artículos de la colección termina en la generación uno antes de ser recolectada. Esto me lleva a en regla de optimización importante. Nunca debes agregar. Finalizar er's a objetos en las raíces, a menudo gráfico de objetos solo dos objetos en el borde. Por último, veamos la última observación del pelaje. Vimos que después de presionar la tecla, sólo ocho objetos se finalizaron antes de que finalizara la aplicación. ¿ Qué pasó ahí? En realidad, la respuesta es muy simple. Cuando un proceso está saliendo, el tiempo de ejecución neto DOT da a la amenaza de finalización un máximo de cuatro segundos para procesar cualquier objeto restante en la que de finalización después de este plazo. Todo el proceso termina en todos los objetos restantes en la Q r D asignados sin llamar a su Isar final en esto me lleva al punto final sobre Finalize er's. No se garantiza que se les llame si el proceso host está saliendo de cualquier cosa que quede en la finalización que Después de cuatro segundos, es descartado. De acuerdo, así que vamos a resumir las desventajas de Finalizar er que hemos visto hasta ahora. Finalizar er's son enfrías en un orden aleatorio. Objetos con Finalizar er siempre terminan en la generación uno en, a veces incluso en generación a cuando el proceso termina, algunos finalizan er's podría no ser llamado, y aquí hay un resumen apagado. Los temas que hemos cubierto en esta conferencia finalizan ER son métodos de clase que son llamadas cuando los objetos están a punto de ser limpiados por el recolector de basura. Muchos objetos pequeños, de corta vida con Finalize Er son malos porque el Isar final extiende su vida a la generación uno. Sólo se debe agregar, finalizar er a los objetos en el borde, a menudo gráfico de objetos. Finalizar. Er's nunca debe hacer referencia a otros objetos. Finalizar er debe ser extremadamente corto. Las manos corren muy rápido 11. Consejo #3: utiliza el patrón de desequilibrio: el recolector de basura en redes de puntos es muy útil en que está constantemente escanea el montón busca de objetos D referenciados y los limpia en ciclos de recolección discretos. A medida que los programadores se debilitan, simplemente asignan objetos cuando los necesitamos y olvidan de ellos. Cuando terminemos, el recolector de basura nos seguirá por ahí, por así decirlo ons limpiar nuestro desorden cuando en objeto necesita asignar algunos tipos fuera de escasos recursos, Digamos, un manejador de archivo nativo o un contexto del dispositivo de gráficos o un bloque fuera de la memoria nativa, Entonces el lugar perfecto para hacerlo es en el constructor. En el momento en que el objeto esté listo para su uso, el recurso también estará listo y disponible. contrario, el mejor lugar para liberar el recurso es en el Isar final. El recolector de basura llama automáticamente a los métodos finales de Isar justo antes de que los objetos obtengan el asignado. Haciendo, así garantiza que el recurso se libera en el momento exacto en que el recolector de basura está a punto de desasignar el objeto. No obstante, este enfoque tiene un gran problema. El tiempo entre la construcción al finalizar puede ser enorme. El recolector de basura es lento en intenta correr lo menos posible. De hecho, si hay suficiente memoria del sistema disponible, es posible que el recolector de basura no se ejecute. Eso es todo, prefiriendo dejar que la memoria se llene lentamente a lo largo de periodos días libres o semanas, manteniendo un recurso escaso asignado por días. Es una práctica terrible que mata toda escalabilidad en. Lo que lo empeora es que probablemente ya no se esté utilizando el objeto y ya ha vencido al referenciado. Entonces, ¿cómo solucionamos este problema? Esta solución es utilizar el patrón de disposición. Este patrón de diseño introduce un mensaje dispuesto para su liberación explícita. El recurso es, echemos un vistazo a un poco de frío. Aquí hay un pequeño programa que escribí dos demuestra las ventajas fuera del patrón dispuesto. Empecemos con el mensaje principal del programa aquí. Tengo dos declaraciones de campos estáticos en la parte superior, objetos finalizados que mantendrán el número total fuera de los objetos finalizados y el tiempo total, lo que mantendrá libre la vida total. Todos los objetos se combinan en milisegundos. Entonces aquí en este bucle, asigno 500 miles de objetos en una llamada ahí a métodos de trabajo para simular algunos tipos fuera del trabajo que se está haciendo. Entonces, cuando termina el bucle, muestro el número total de los objetos finalizados en el número promedio fuera de milisegundos que el objeto estaba vivo. Echemos un vistazo a la clase sin disposición. Es una clase muy sencilla que asigna e inicia un cronómetro en It's constructor. El mensaje do work es un bucle simple que hace algún tipo de cálculo apagado para simular el trabajo se está haciendo en el Isar final aquí abajo detiene el cronómetro y actualiza los dos campos estáticos en el programa principal. Tenga en cuenta que uso la clase entrelazada para actualizar los campos estáticos de manera segura contra amenazas porque podría haber más de un hilo tratando de actualizar los campos al mismo tiempo. La clase entrelazada se asegurará de que eso es todo. Los hilos se ejecutan en secuencia y ninguna condición borrada puede una cura. De acuerdo, déjame ejecutar el programa. Se pueden ver los resultados. Aquí vamos. Estos son los resultados. El programa logró finalizar 455,000 objetos, lo que significa que los 45 mil objetos restantes siguen en el montón en generación cero a la espera de ser recolectados. El tiempo de vida promedio a menudo objeto es de 736 milisegundos. Por lo que este es el promedio de tiempo de desconexión entre la construcción al finalizar. Entonces aquí está nuestro problema estamos fingiendo que son objetos, asigna algún tipo de recurso escaso. Pero esas fuentes re se están reteniendo durante 736 milisegundos antes de ser liberados ums. En cualquier momento dado, hay 45 mil objetos de referenciados no recolectados en el montón en espera de ser recolectados colillas todavía aferrándose a su recurso. Esto va a ser terrible para la escalabilidad. Podemos solucionar este problema mediante el uso del patrón de disposición. Echa un vistazo a esta clase aquí llamada con Dispose en la que he implementado el patrón . Se puede ver que el constructor sobre los métodos do work son exactamente los mismos. Pero mira esta interfaz aquí. El patrón dispuesto requiere que implemente la interfaz desechable I. El inter barda define un único método de disponer en el que se supone que debes liberar explícitamente cualquier asignado. Escaso recurso es que he implementado mis métodos de disponer aquí. Mi mensaje simplemente llama a otros métodos de disposición virtual protegidos. Aquí abajo está el Isar final, que también llama a los métodos virtuales eliminados protegidos. Se puede ver que el Isar final proporciona un argumento falso sobre los métodos dispuestos públicos, proporciona unos argumentos verdaderos. Entonces dentro de los métodos eliminados protegidos no puedo saber distinguir si el código se está enfriando del Isar final o de los métodos desechados públicos. Esto es muy útil porque podría recordar de la conferencia sobre Finalizar er's que hay muchas cosas que no se le permite hacer dentro del Isar final como, por ejemplo, acceder a referencias a otros objetos. Entonces si este argumento booleano es falso, sé que el código se está ejecutando en la amenaza final de Isar en. No puedo acceder a otros objetos. Pero si el argumento es cierto, entonces este método fue llamado desde códigos de usuario y todo está bien. Puedo hacer lo que quiera. También hay un booleano desechado aquí que asegura que el abrigo desechado solo se ejecute una vez . Entonces si por alguna vez fue razón alguien sigue llamando al público métodos desechados una y otra este abrigo sólo ejecutará una vez. Por último, me gustaría mostrarles un truco más cool. Imagínese por un segundo que los métodos dispuestos han sido llamadas a los objetos ha liberado. Su recurso asignado es por lo que ahora no hay necesidad de que el Isar final se ejecute. Eso es todo porque no queda nada que hacer. Claro, el Isar final en realidad no haría nada porque la variable dispuesta ya está establecida verdadera. Pero recuerda, un Isar final extenderá el tiempo de vida fuera de cualquier objeto a la generación uno. Esto pondrá presión sobre el recolector de basura sin motivo alguno. Afortunadamente, hay una solución muy cool. En realidad puedes apagar el Isar final para cualquier objeto. Esta línea de códigos aquí desactivará el Isar final si los métodos de disposición se han corrido correctamente sin errores. Por lo que después de llamar a disposición, los objetos efectivamente ya no tienen un Isar final, el recolector de basura puede descartarse de forma segura en el próximo ciclo electoral y el objeto nunca será promovido a generación uno. De acuerdo, entonces voy a hacer algunos cambios en el programa para utilizar los nuevos objetos que implementa el patrón dispuesto. En primer lugar, te mostraré lo que pasa cuando simplemente cambie el tipo de objetos que se están creando ons. No hacer nada más. Déjame ejecutar el programa. Aquí vamos. Y aquí están los resultados más o menos los mismos que la corrida anterior. 462,000 objetos siendo finalizados y en promedio, vida útil fuera 639 milisegundos. Ahora tengo este mensaje fresco dispuesto, pero en realidad no lo estoy llamando. Por lo que el recurso sigue siendo liberado en el Isar final en el recurso promedio. La vida útil es más o menos la misma que antes, así que arreglemos eso. Voy a cambiar la forma en que los objetos se asignan dentro del bucle. Usaré la declaración de uso que garantiza que los métodos de disposición obtienen llamadas al final del bloque de uso. Entonces esto debería hacerlo ahora. En cada nueva aireación de tono, el objeto se crea, se utiliza, luego se elimina inmediatamente. Yo me aferro al recurso es para lo más brevemente posible. Voy a volver a ejecutar el programa. Echa un vistazo a esto y aquí están los resultados. El programa ahora ha desechado todos los 500,000 objetos porque el bucle dispone cada objeto antes de iniciar la siguiente iteración. En el promedio de vida libre, el recurso ha bajado a un asombroso 0.26 milisegundos. Eso es 245,000 veces menos que el código que no usó el patrón de disponer. Se pueden visualizar los resultados así. Si este gráfico horizontal representa el tiempo, podemos dibujar una línea todo el camino de la izquierda cuando el objeto se construye en otra línea todo el camino de la derecha, donde el objeto se finaliza. Pero este punto aquí es cuando terminamos con el objeto encendido a punto de dejarlo salir de alcance . Es vital llamar explícitamente a disponer en este punto, porque de lo contrario estaríamos aferrándonos al recurso es para todo el tiempo de descanso entre de referenciar en la finalización, que es 245,000 veces mayor que el tiempo que nosotros realmente necesitaba el objeto en primer lugar. Aquí un resumen de lo que hemos aprendido en esta conferencia. El patrón dispuesto proporciona un mensaje para liberar explícitamente el recurso escaso es que las sentencias de uso llamarán automáticamente al método dispose al final fuera del bloque de uso . El patrón de disposición reduce drásticamente el tiempo que el recurso es retenido por los objetos. Llamar a los métodos de disponer suprime el Isar final, lo que impide que la vida de los objetos se extienda a la generación uno 12. Consejo #4: evitar el boxeado y el boxing: Te voy a enseñar a mejorar las asignaciones de memoria en tu abrigo. En esta conferencia, vamos a echar un vistazo a los ons de boxeo. Unboxing. Tal vez te estés preguntando si el boxeo Tía Unboxing tiene consecuencias negativas como, por ejemplo, aumento de la presión sobre el recolector de basura. Bueno, vamos a averiguarlo. Escribí un programa para medir la diferencia en los patrones de asignación de memoria entre un fragmento de código que no usa boxeo versus un fragmento de código que sí usa boxeo. Aquí está la costa. Voy a empezar con códigos que no usa boxeo. Entonces aquí está el método principal del programa, con un bucle simple que llama a un método de prueba un millón de veces. Se puede ver que paso el índice de la bomba actual al mensaje de prueba hacia arriba. Aquí está la declaración de métodos de prueba. El mensaje de prueba, excepto en los parámetros entrevistados ons, no hace absolutamente nada. Voy a crear una boletas liberadas fuera de este programa en mono desarrollar mediante el uso del menú construido . Aquí vamos. De acuerdo, así que ahora tengo en ejecutar archivo herbal en la carpeta bin slash release off my projects. El siguiente paso es ejecutar manualmente este programa en la línea de comandos en uso El impresionante perfilador de aspecto que está integrado en el marco del modelo. Déjame cambiar a la línea de comandos ahora para ejecutar un programa mono. Todo lo que necesitas hacer es llamar al gemido. Ah, ejecutar herbal Andi como los primeros argumentos proporciona el nombre de archivo herbal ejecutar que desea ejecutar para habilitar el Profiler. Todo lo que necesitas hacer es agregar este parámetro de configuración aquí El parámetro de perfil habilita el perfilador de mirada en la opción de reporte indica que no quiero un archivo de registro en disco. Simplemente quiero ver el informe de rendimiento directamente en la línea de comandos. De acuerdo, entonces aquí vamos. Voy a ordenar sobre el programa en Estos son los informes de desempeño. Vamos a pasar por las secciones una por una, aparezcan los datos de la versión de él. Estoy usando la versión cero puntos cuatro fuera del perfilador, La instrumentación de perfilado introducida en gastos generales extra fuera 60 nanosegundos en mi abrigo que no es que las manos. Se puede ver que ejecuto el programa el 4 de septiembre de 2015. A continuación se presenta la sección jit. El compilador justo a tiempo compila ocho métodos en aproximadamente un kilobytes fuera del lenguaje nativo de la máquina , y aquí las cosas se ponen interesantes. El recolector de basura no se calentó precisamente. Se movió 21 objetos durante la compactación del montón. Hans realiza dos ciclos de recolección en la Generación uno, que en promedio tardaron 1146 microsegundos cada uno. Podría estar preguntándose por qué no hay colecciones Generation zero. Bueno, es porque mi programa no asigna nada en el montón en absoluto. Todos los objetos en la memoria son del tiempo de ejecución de punto net en sí, y se crearon antes de que mi programa incluso comenzara. Por eso todos terminaron en la Generación uno. El resumen de asignación muestra una lista de todos los objetos asignados mientras el programa se ejecutaba . El listado está lleno de objetos de limpieza, pero se puede ver para inter jours aquí abajo. Uno de estos dos es nuestra variable de bucle. Por lo que en total, se han asignado 33 objetos, los cuales ocupan 3496 mordeduras memoria de calor. De acuerdo, No, voy a hacer un cambio en el programa, revisar sus celdas. Voy a cambiar el tipo fuera de los argumentos fuera de los métodos de prueba a objetos. Por lo que ahora, durante cada bucle, es oracion. El entero necesita estar en caja en el montón, Entonces ese es el tipo de objeto. Los argumentos pueden referirse a ella. Veamos qué tipo de impacto va a tener este cambio de código a las estadísticas de asignación de memoria . Voy a construir el programa de nuevo para actualizar el archivo herbal ejecutar en disco en. Después cambio a la línea de comando en una carrera. El programa con perfilado de mirada otra vez. Aquí vamos. Estos son los resultados. Echa un vistazo a las diferencias. El recolector de basura ahora realizó cinco generaciones cero colecciones ons a colecciones Generación uno . El tiempo promedio por colecta es aproximadamente el mismo, pero eso es más del doble del número de colecciones que teníamos antes. Pero la mayor diferencia está en el resumen de asignación. Ahora tengo 1 millón de libras para introducir asignado. ¿ Estás comprando 24 megabytes de memoria? El montón ahora contiene un millón en 33 objetos que abarcan 24 megabytes de memoria. Esto es muy interesante porque probablemente sepas que un entero tiene sólo cuatro bytes longitud, pero un entero de bloques en el montón ocupa 24 picaduras, por lo que estamos usando seis veces más memoria que el escenario. Sin boxeo, la diferencia es bastante dramática. Pasé de cuatro kilobytes fuera de los asignadores. Calentar la memoria a 24 megabytes y de introducir a un millón de hormigas también, lo que requirió cinco colecciones adicionales Generación cero para quitar todo de la cadera. Por lo que la primera regla para mejorar las asignaciones de memoria en tus códigos es evitar el boxeo en unboxing donde puedas, porque tiene la indeseable tendencia a inundar absolutamente el montón con montones y montones de objetos pequeños. De acuerdo, aquí un resumen de lo que tenemos aprende en esta conferencia boxeo ons. Unboxing es un proceso que te permite usar tipos de valor en tipos de referencia intercambiablemente en tu abrigo. Cada operación de boxeo crea un nuevo objeto en el montón. Un objeto de caja ocupa más memoria que el tipo de valor original. boxeo puede inundar el calor con montones de objetos pequeños encendidos. Poner presión considerable sobre el recolector de basura. El boxeo en el unboxing debe evitarse siempre que sea posible. 13. Consejo #5: no añadir muchos cuerdas: Lo bonito de esta sección es que puedes leer la conclusión directamente desde el título de la conferencia. Entonces al parecer no se supone que inventes cuerdas innatas juntas. Pero, ¿por qué no? ¿ Qué tiene de malo unir unas cuerdas juntas? Bueno, vamos a averiguarlo. He escrito un pequeño programa de prueba para investigar los efectos que tendrá la concatenación de cadenas en los brazos de asignación de memoria, basura, basura, comportamiento de colector. Echa un vistazo a esto. Tengo el mensaje principal del programa aquí en todo lo que hace es llamar uno a los métodos de prueba. El primer método se llama string Tests, y se declara aquí arriba. Se puede ver que eso es todo lo que estoy haciendo es construir una cadena de 10 miles de caracteres que consta enteramente de caracteres hash. Empiezo con una cadena vacía y luego en un bucle, agrego un solo carácter 10,000 veces. Ahora lo primero que quiero hacer es configurar unas mediciones basales. Por lo que voy a comentar la llamada a la prueba de cuerdas en el mensaje principal aquí. Entonces ahora el programa no hace absolutamente nada, y podemos observar cómo se comporta el recolector de basura en esta situación. También podemos determinar cuántos objetos hay presencia en el montón por defecto. Voy a construir el proyecto usando el menú build encendido, luego cambiar a la línea de comandos para poder ejecutar el programa con el perfilador de mirada que está integrado en el framework Mona que has visto en la conferencia anterior. Eso es todo lo que necesitas hacer es llamar al Ejecutar Herbal con los comandos gemidos Oh, luego proporciona estos argumentos de perfil para habilitar el perfilador de registro en Establecer it para mostrar los informes de rendimiento directamente en la consola. De acuerdo, aquí vamos. Estoy ejecutando el programa de línea base. No, y aquí están los resultados. El recolector de basura realiza dos colecciones Generación uno. el montón hay 19 cuerdas, que ocupan un poco más de un kilobytes de memoria. La memoria total del montón es de 3496 bytes asignados por 33 objetos. Entonces ahora volveré a cambiar a modelo desarrollar una NCAA, significó la llamada a string test, reconstruye el programa, cambiar de nuevo a la línea de comandos y ejecutar el programa de nuevo. Veamos qué tipo de impacto va a tener la concatenación de cuerdas. Consulta esta otra cosa. Mira esto. Ahora hay tres generaciones, cero colecciones en colecciones de siete generaciones una en el resumen de asignación, podemos ver que hay 10 miles y 20 cadenas fuera del montón, ocupando alrededor de 100 megabytes de memoria. También hay un mensaje Cool resumen sección aquí abajo. Podemos ver que hicimos 10,000 llamadas al método string dot com cat, que es exactamente como se esperaba. Pero los métodos de los gatos caracola, llamados otro métodos enfría las asignaciones internas. Str Este método se llamó 9999 veces a esto dio como resultado casi 15,000 operaciones de copia de memoria . ¿ Qué está pasando aquí? El motivo de este comportamiento es que los flujos son objetos inmutables en dot net. pocas palabras, esto significa que los datos de cadena nunca se pueden modificar de ninguna manera. Ahora bien, esto podría sonar un poco raro porque si fuera así, ¿cómo pueden funcionar métodos como reemplazar a los gatos Khan? Bueno, la respuesta es que esos métodos no modifican directamente la cadena. En cambio, crean en cadena completamente nueva en el montón con todas las modificaciones. Por lo que esta línea, dejaremos la variable de texto completamente sin cambios. Andi en su lugar crea una copia fuera del texto antes de los reemplazos en esta copia encendido, luego devuelve la copia y las historias en los resultados. Variable. Esta es una regla de oro. Off string operaciones en dot nets string message. Nunca modifique la cadena original. En cambio, siempre hacen una copia. Modificar la copia en su lugar al devolver esa copia. En consecuencia. El motivo de este comportamiento es que hace que las cadenas se comporten como tipos de valor, lo que significa que se pueden asignar y comparar por valor, lo cual es muy conveniente para los desarrolladores. Nunca tienes que preocuparte si a las variables de cadena podrían estar refiriéndose a la misma cadena en el montón. Además, permite que el tiempo de ejecución de dot net realice alguna optimización fresca en códigos de manejo de cadenas para mejorar el rendimiento. Entonces en mi programa de pruebas, esto es lo que pasa. Empiezo con una cuerda vacía. Cuando agrego el primer carácter. El método calm cat no modifica la variable string. En su lugar, hace una copia fuera de la secuencia, agrega el carácter a la copia y asigna la copia de nuevo a la variable de cadena. En el siguiente bucle, se aireación. Otra vez pasa lo mismo. Se copia la cadena de un carácter. Se agrega un segundo carácter a la copia en la copia se asigna de nuevo a la variable de cadena Después de 10 miles de concatenación. Tendré 9999 d cadenas referenciadas en el montón, más una cadena activa con el resultado final. Esto es enormemente ineficiente, por lo que resulta que las cadenas en realidad están optimizadas para una comparación rápida, y no son muy buenas en las modificaciones de libro para realizar muchas modificaciones a una sola cadena. Hay una clase mucho mejor diseñada específicamente para ese propósito. El constructor de cuerdas, un constructor de cuerdas, se comporta más como lo que esperarías que una cadena sea un buffer de caracteres en la memoria al que puedes derecho libremente cuando te tía, un personaje dedo del pie un constructor de cuerdas. No hay copia pasando detrás de bambalinas. En cambio, el marco de red de puntos simplemente escribe ese carácter directamente en la cadena. Entonces modifiquemos el programa para usar un constructor de cadenas en su lugar y veamos cómo eso influye en el patrón de asignación de memoria. Aquí está mi programa otra vez. Hay otro método de prueba llamado String Builder test. Se puede ver que básicamente es el mismo abrigo, pero usa un constructor de cuerdas en su lugar para subir los 10,000 caracteres. Cambiaré los principales métodos del programa para enfriar los métodos de prueba del constructor de cadenas en su lugar. De acuerdo, así que ahora de nuevo, la rutina familiar fuera de construir el programa, cambiar a la línea de comandos al ejecutar el programa con perfilado de registros lo habilita. Aquí vamos. Y aquí están los resultados. El recolector de basura está de vuelta a sólo dos colecciones de generación uno, lo cual es idéntico a nuestras mediciones basales. Por lo que los abrigos constructor de cuerdas no colocan presión extra. Bueno, el recolector de basura lo que sea. Fantástica tía, Aquí está el resumen de asignación. Ahora tengo 31 cuerdas ocupando un poco más de 64 kilobytes. Se trata de una mejora masiva. Pasé de 100 megabytes a 64 kilobytes, que es un 99.9% mejoras en las huellas de memoria. Pero aguanta. Mi cadena eventualmente contiene 10 miles de caracteres justo en un carácter Unicode en .net ocupa dos picaduras. Entonces, ¿por qué tengo 64 kilobytes asignados por 31 cadenas? ¿ No debería mi cadena ser solo de 20,000 bytes? Y la respuesta es que el constructor de cadenas inicialmente solo tiene una minúscula cantidad de memoria de caracteres disponible. Cuando el búfer está lleno, el constructor de flujo asigna un nuevo buffer el doble del tamaño ons copia todos los datos en este nuevo buffer. Entonces, para 10,000 caracteres, pasamos progresivamente por tamaños de búfer de uno a cuatro ocho en 16 kilobytes hasta llegar eventualmente a 32 kilobytes, cual es suficiente para contener 10,000 caracteres Unicode. El algunos apagado 1248 16 y 32 es de 63 kilobytes. Exactamente lo que estamos viendo en el montón en este momento Para asegurarme de asignar solo la cantidad exacta requerida de memoria necesito inicializar el constructor de cadenas al tamaño correcto. Si el búfer de caracteres comienza en 10 000 caracteres de inmediato, no hay necesidad de que se duplique de tamaño mientras el bucle se está ejecutando. Esto acelerará mi programa y reducirá las huellas de memoria para implementa el cambio . Todo lo que necesito hacer es tía un argumento constructor aquí especificando la capacidad inicial fuera del constructor de cadenas en caracteres. Lo inicializaré a exactamente 10 miles de caracteres, así que todo encaja bien, pero construimos un cambio de programa a la línea de comandos encendida para ejecutar el programa con perfilado Lok . Aquí vamos. Estos son los resultados 22 cadenas que ocupan un poco más de 20 kilobytes. la misma manera que esperábamos, acabo de quitar otro 70% de descuento en asignaciones de memoria innecesarias. Aquí un resumen de lo que tenemos aprende en este mensaje de cadena de conferencias. Nunca modifique la cadena original. En cambio, hacen una copia. Modificar la copia tía return Eso es café Las cuerdas están optimizadas para comparaciones rápidas. La clase constructor de cadenas está optimizada para modificaciones rápidas. Usar siempre constructores de cadenas al modificar cadenas en Código de Misión Crítica En mi programa de prueba , cambiar de cadenas a constructores de cadenas redujo las huellas de memoria en 99.9%. 14. Consejo #6: utiliza obras en lugar de las clases: en esta conferencia, voy a echar un vistazo más de cerca a Stratch's ¿Qué son exactamente los golpeados y cómo se diferencian de las clases? Muchas personas se confunden cuando tienen que explicar la diferencia entre los de Struck y las clases. Vamos a pasar por ellos uno por uno. Cubrimos la diferencia entre tipos de valor sobre los tipos de referencia en la sección fundamental para re valor de copa. Los tipos se almacenan en línea ya sea en la pila o en el montón, mientras que los tipos de referencia siempre se refieren a un objeto que se almacena en otro lugar de la Web. Volveremos a esta diferencia más adelante en esta conferencia, donde verás que tiene un efecto dramático en la huella de memoria en el comportamiento de los recolectores de basura . Debido a que los trazos son tipos de valor, se asignan y se comparan por valor. Cuando asignas Stratch's el tiempo de ejecución neto de puntos crea un struck enteramente nuevo ons. Copias todo se siente terminado. Compara esto con una clase donde la referencia sus copias sobre Andi terminas con dos referencias al mismo objeto en el Heeb. Esto nunca puede suceder con los de Struck, porque los trazos son tipos de valor que no pueden heredar de una clase base, eres libre de implementar interfaces pero no puedes heredar ningún tipo de implementación. Otra restricción un tanto extraña es que los campos internos no pueden haber inicializado Eer's. Todos los campos se inicializan a su valor predeterminado automáticamente, y solo se pueden anular sus valores en el constructor. Y hablando de constructores, un golpeado no puede tener un parámetro menos constructor. Debe declarar al menos un argumento. El motivo de esta restricción aparentemente extraña es que el tiempo de ejecución inicializado instruye poniendo a cero su memoria. En contraste, las clases se inicializan llamando a su constructor predeterminado. Esto hace que el inicializado de Stratch's sea mucho más rápido, y luego las clases y finalmente los golpeados no pueden haber finalizado er's. Esto es bastante obvio cuando se considera que ese golpe es nuestro valor. Tipos en el recolector de basura, que es responsable de ejecutar Finalize Eer's siempre ignorará los tipos de valor. Esto puede hacerlo de forma segura porque los tipos de valor no necesitan ser limpiados explícitamente. Piénsalo. Cuando un tipo de valor reside en la pila, se limpiará cuando el frío regrese del método y se descarte el marco de pila correspondiente , y cuando un tipo de valor resida en el montón, se limpiará cuando es tipo conteniendo es recogido por el recolector de basura. Esto es algo importante para recordar el recolector de basura Onley marca y barre tipos de referencia. Siempre ignorará los tipos de valor, independientemente, cómo se utilicen. De acuerdo, así que esa fue una breve introducción sobre la diferencia entre los de Struck y las clases. Ahora veamos cómo se comportan en un programa de prueba real. Echa un vistazo al siguiente código. Tengo Razón. Un programa de prueba corto que asigna un millón de objetos puntuales en los almacena en una lista genérica . Mis objetos de punto son contenedores simples para una X en un valor Y. A menudo ves este tipo de clases en programas que necesitan realizar un seguimiento de píxeles, coordenadas de mapa o vectores matemáticos. Entonces mi mensaje principal de programa aquí abajo enfría un solo método de prueba llamado clase de prueba. El método de clase de prueba de aquí localiza clases de un millón de puntos en un bucle. Inicializar son ellos usando el contador de bucle. Andi los agrega a esta lista genérica. Esta es la declaración de clase para la clase de puntos. puede ver que es muy sencillo solo a campos públicos para las coordenadas X e Y . Onda, un constructor para inicializar los puntos. De acuerdo, Antes de que voy a ejecutar este programa, necesito unas mediciones de línea base para ver qué tipo de objetos están presentes en el montón por defecto. Por lo que voy a comentar la llamada a los métodos de la clase de prueba en el mensaje principal del programa . Ahora el programa no hace absolutamente nada, y podemos usar esto para crear una línea de base para nuestro reporte de perfil. Estoy creando una compilación de lanzamiento ahora, Entonces cambie a la línea de comandos para que pueda ejecutar el programa con el perfilador de registro. Aquí vamos. Y aquí están los resultados que tenemos a Generación uno colecciones por el recolector de basura. Hay 21 movimientos de objetos debido a ciclos de compactación de montón en el contiene 3552 picaduras asignadas por 33 objetos. De acuerdo, vuelta a los códigos. Voy a eliminar los comentarios para que podamos ejecutar una medición para la implementación que utilice clases. Veamos cómo se ve el montón después de asignar clases de un millón de puntos. De acuerdo, crea una liberación facturas cambiar a la línea de comando ons Ejecutar el programa. Aquí vamos. Y aquí está nuestra respuesta. Este código pone bastante carga en el recolector de basura. Tenemos colecciones de cuatro generación cero y tres de generación una colecciones más de tres veces más colecciones que la medición de línea base. El recolector de basura realiza 870,224 movimientos de objetos mientras compacta el montón. Eso está mucho fuera de las operaciones de copia de memoria, lo que afectará negativamente el rendimiento de este programa en Mira el resumen de asignación aquí mi matriz está aquí, una sola instancia que ocupa megabytes de SIDA fuera del espacio de calor. Esto es lo que esperaría porque en los objetos la referencia en un sistema de 64 bits es de ocho bytes tamaño, un millón de objetos por ocho bytes es de ocho megabytes, y aquí están las instancias de puntos. Tengo un millón de puntos ocupando 24 megabytes fuera de espacio de montones. Por lo que en total, mi programa asignó 32 megabytes de él la memoria. Por último, en los métodos llamados sumario debilitar ver el tiempo total de ejecución fuera del programa. Se trata de dos puntos 37 segundos. Ahora quizá te estés preguntando cómo un millón de puntos suman hasta 24 megabytes. Mi clase de puntos solo contiene dos enteros de cuatro bytes cada uno, por lo que esperarías que un millón de puntos ocupe solo megabytes del SIDA. ¿ Qué pasó aquí? La respuesta es que cada objeto introduce 16 bytes de sobrecarga, que se utiliza para datos de limpieza, lo que cada punto tiene en realidad 24 bytes de tamaño, tres veces más grande de lo esperado. Veamos si puedo hacer que este programa sea más eficiente usando Struck en lugar de clases, voy a cambiar el mensaje principal del programa para llamar a los métodos test struck. En cambio, se puede ver por la declaración que el método es prácticamente el mismo que la clase ocho de prueba asigna un millón de puntos y los almacena en una lista. La única diferencia es que ahora estoy usando puntos tachados en lugar fuera clase de puntos. Entonces, ¿qué es un punto golpeado? Consulta aquí la declaración. Es simplemente instruir con los campos X e Y públicos en un constructor para inicializar los campos. Mira cuando cambio rápidamente entre la clase en la estructura. Ves, son más o menos idénticos. De acuerdo, vuelta al mensaje principal del programa. Voy a cambiar el mensaje de prueba. Llama a usar Stacke's en su lugar, y ahora el ritual habitual crea un billetes. Cambie a las manos de la línea de comando ejecutar el programa. Tuvo otros resultados. Estamos de vuelta a dos colecciones de generación una y 21 movimientos de objetos, lo cual es idéntico a la línea de base. Por lo que ahora no hay presión en los recolectores de basura ni en lo que sea. El resumen de asignación muestra la matriz que ocupa ocho megabytes de datos y nada más. Por lo que ahora la memoria total del montón es de sólo ocho megabytes, un asombroso cuatro veces. Mejoras en huellas de memoria. ¿ Cómo sucedió eso? Para entender la diferencia, hay que visualizar cómo se almacenan los datos en la memoria. Empecemos con el código de clase de puntos. Una lista de un millón de puntos clases se ve así en el montón. Cada elemento de la lista es una referencia de ocho bytes a una instancia de clase de punto en otro lugar de la Web, y cada instancia de clase de punto consta de 16 bytes de los datos de limpieza. Tía ocho muerde datos reales de campo, por lo que 24 mordeduras en total. Todo esto suma hasta ocho megabytes para la lista en 24 megabytes para las instancias de puntos, o 32 megabytes en total. Ahora compare esto con el golpeado. Implementación, como ustedes saben, instruye a nuestras tiendas en línea, por lo que esto significa que se almacenan directamente en los propios artículos de la lista. No hay referencias en este escenario. Cada golpeado tiene cero sobrecarga y ocupa sólo ocho mordeduras de su memoria para los dos enteros. Por lo que toda la matriz sigue siendo de ocho millones de bytes, pero sin ningún objeto adicional sobre el así. Esto reduce las huellas de memoria a ocho megabytes, un cuádruple de mejoras. Porque ahora solo hay una sola matriz en el montón. El recolector de basura no tiene que hacer ningún trabajo extra. Simplemente limpia la matriz porque los puntos se almacenan dentro de la matriz. Esto también limpia todos un millón de puntos de forma gratuita. Por último, veamos el tiempo de ejecución del programa. tiempo total de ejecución es ahora de solo 322 milisegundos. El motivo de esto es que esta implementación no tiene que llamar a un millón de puntos Tienda Constructores. Un millón de referencias de montón en la matriz. Onda mueve 870,000 objetos. Dos. Compacto, El montón que ahorra mucho tiempo. De acuerdo, entonces los de Struck son geniales, y siempre deberíamos usarlos ¿verdad? Bueno, sin estructura, genial, pero solo debes usarlos en escenarios muy específicos. Aquí tienes una lista. Usar trazos Cuando los datos Tu historia representa un solo valor. Ejemplos son un punto. Un vector sobre matriz, un número complejo, una parte de valor. Un nuevo alumno médico etcétera, usa puntales. Si el tamaño de tus datos es muy pequeño, 24 picaduras o menos. Andi, ibas a crear miles o millones fuera de instancias. Usa Stratch's. Si tus datos son inmutables y deben asignarse y compararse por valor, usa Stac's. Si tus datos no tendrán que ser boxeados con frecuencia en todos los demás escenarios, las clases son una mejor alternativa. Entonces, para resumir, Struck es nuestro tipo de valor. Las clases son tipos referenciados. golpeados son mucho más rápidos que las clases porque no tienen un constructor predeterminado. No se les puede heredar tía. No son recogidos por el recolector de basura. Strat's solo asigna memoria de montón para sus campos internos, y no tienen la sobrecarga de 16 mordeduras que tienen los objetos en el calor en ciertos escenarios. uso de golpeados en lugar de clases puede reducir drásticamente la huella de memoria en el tiempo de ejecución fuera de tu abrigo. Mi programa de prueba con Stratke's localizé cuatro veces menos memoria y corrí siete veces más rápido, luego la implementación que usaba clases 15. Consejo #7: preasignar colecciones: en esta conferencia, me gustaría centrarme en un aspecto interesante fuera de las colecciones que a menudo se pasa por alto. Pero primero, echemos un vistazo atrás a la conferencia de concatenación de cuerdas anterior, recordarás que miramos onda de concatenación de cuerdas. Descubrimos que las cuerdas son inmutables. Un montón de caracteres pendientes a una cadena resultó en una huella de memoria terrible. Debido a que toda la cadena se copió en el montón durante cada concatenación, solucionamos el problema mediante el uso de un constructor de cadenas en su lugar. Un constructor de cuerdas es un buffer de caracteres en la memoria, con una cierta capacidad que puedes directamente derechos en reemplazar cadenas con constructores de cuerdas hizo una enorme mejora a la huella de memoria fuera de mi abrigo. Eso es ¿recuerdas lo que pasa cuando ejecuté los códigos del constructor de cadenas por primera vez . Mi cadena de 10 miles de caracteres terminó ocupando 64 kilobytes fuera de espacio de montones. El motivo de esto es que el constructor de cuerdas inicialmente solo tiene un carácter minúsculo de cantidades fuera . Memoria disponible Andi. Cuando el buffer se queda sin espacio, simplemente se asigna. Un nuevo buffer off El doble del tamaño ons copia todo en el nuevo buffer. retamaño consecutivo está apagado 1 a 4, 8 16 y 32 kilobytes resultó en 63 kilobytes de espacio de calor asignado, mucho más de lo que realmente necesitaba para el arroyo. Ahora aquí hay una regla importante. Toda colección en la lista de redes de interior hace lo mismo. inicia una lista con cierta capacidad por defecto en cuando se queda sin espacio, se creará una nueva lista fuera del doble del tamaño que las manos copian todo. Esto inflará las huellas de memoria de nuestros códigos y afectará negativamente el rendimiento. Porque fuera de todas las operaciones de copia de memoria que suceden entre bastidores, hagamos un montón de mediciones para averiguar cuántos gastos generales de memoria introducirá el cambio de tamaño de lista . Echa un vistazo a este código. Declaro un montón de colecciones aquí arriba. Honore list a que It's Stuck, una lista genética en el diccionario. Mi programa principal, Messes, llama a un solo método llamado en sus colecciones, que agrega un solo elemento a cada colección. Esto es necesario porque algunas colecciones implementa la carga perezosa. Inicializan su almacenamiento interno solo cuando se agrega el primer elemento. Entonces para forzar que cada colección se inicialice, necesito agregar al menos un elemento a cada una de ellas. Ahora el siguiente paso es un poco funky para averiguar cuál es la capacidad predeterminada. Necesito mirar dentro de las clases de cobranza a su implementación privada interna. No hay manera de que pueda lograr eso en frío excepto usando la reflexión. Pero una forma mucho más fácil es ejecutar mi programa en modo de depuración, establecer un punto de ruptura y luego mirar dentro de las colecciones con el desbridador. Entonces voy a poner un punto de descanso aquí y desde el programa. Ahora puedo usar la ventana del reloj para inspeccionar cada colección. Todo comienza con la lista de arreglos. El array interno será un miembro privado, así que necesito ampliar la carpeta de Miembros no públicos aquí en aquí. Está en Object Array llamado elementos con una longitud off. Cuatro. Siguiente es el taco. El arreglo de almacenamiento interno es de nuevo un miembro privado llamado Array, con la longitud fuera cuatro. Siguiente es el Stuck. El arreglo de almacenamiento interno está en arreglo entrevistado llamado Array, con una longitud de 16. A continuación, la lista genérica. El arreglo de almacenamiento interno es, um, matriz entrevistada llamada ítems con la longitud o cuatro, y finalmente, el diccionario. El diccionario tiene muchas matrices internas, pero me centraré en este aquí llamado ranuras clave, que tiene una longitud de 12. Entonces para resumir una lista de rayos cuatro artículos que cuatro artículos pegados. 16 ítems listan cuatro ítems. Diccionario, 12 ítems. Se puede ver que se trata de diminutas capacidades iniciales. Si comienzas a agregar cientos o miles de artículos a una colección, tendrá que cambiar el tamaño muchas veces para acomodar todos los artículos. Veamos exactamente qué tan grandes pueden llegar las huellas de memoria. Voy a modificar mi programa para llenar las listas genéticas con unos cientos de miles de ítems. Por lo que aquí abajo en el mensaje principal del programa, voy a comentar la llamada a en sus colecciones. Andi. En su lugar poner en una llamada al método de lista de llenado checkouts. El cine lista métodos. Aquí uso un bucle para agregar exactamente 262,145 enteros a la lista. Entonces, ¿cuánta memoria se requiere para la lista? Cada elemento es un entero de cuatro mordeduras, que es un tipo de valor que se almacena en línea en cada elemento de matriz, Así que la huella total de memoria va a ser cuatro veces 262 miles. 145 es de 1.048,580 picaduras o más o menos un megabytes fuera de almacenamiento. De acuerdo, vamos a probar esa teoría. Voy a crear una tía de compilación de liberación, ejecutar el perfilador de bloqueo para averiguar exactamente cuál va a ser la huella de memoria. Aquí vamos. Ahora estoy dirigiendo el programa. Manos aquí en los resultados. El array entero ocupa 4,195,168 picaduras, o ligeramente más de cuatro megabytes. Lo que pasó aquí es que la lista genética mantuvo re dimensionando su matriz interna una y otra vez hasta que finalmente, todos los 262 miles de artículos caben en la matriz, pero no escogí ese número al azar. 262,145 veces entero de 14 byte es exactamente un megabytes más cuatro picaduras, por lo que el buffer necesita expandirse a dos megabytes para sentir o ítems ons porque ya está ciclado a través de todas las potencias de procedimiento de dos, el montón total asignado memoria es de cuatro megabytes. Esto representa el peor escenario absoluto. Todos los artículos excepto el final caben en la matriz, por lo que la lista tiene que duplicar su tamaño para acomodar la noche final. Um, te quedan con una huella de memoria que es cuatro veces más grande que lo que realmente se requiere. La solución para este problema es súper fácil. Todo lo que tienes que hacer es inicializar la lista al tamaño correcto. Déjame hacer eso ahora mismo en frío. Entonces todo lo que tengo que hacer es cambiar la lista. Declaración fuera de aquí, Andi, agrega el valor del elemento de marcas al constructor fuera de la lista. Ahora necesito crear una nueva compilación de lanzamiento, luego cambiar a la línea de comandos en para volver a ejecutar el programa. Y aquí están los resultados. Las huellas de memoria vuelven a un megabytes, una mejora de cuatro veces. Permítanme resumir lo que hemos visto en esta conferencia. Todas las redes de interior de colecciones se inicializan a una diminuta capacidad predeterminada y duplican automáticamente su tamaño cuando están llenas. Este comportamiento puede resultar en unas huellas de memoria del peor caso que sea cuatro veces mayor de lo necesario. Para evitar este problema, debes inicializar la colección al tamaño correcto 16. Consejo #8: no te materiales expresiones de LINQ a tiempo: en esta conferencia, quiero enfocarme en inesperado lamentable al escribir consultas de enlaces que pueden inflar en gran medida las huellas de memoria de sus códigos. Enlace se introdujo en C Sharp versión cuatro. Andi. Es un lenguaje muy bonito, claro, algo similar a SQL para realizar consultas complejas en innumerables fuentes de datos. Link tiene muchas funciones integradas para el filtrado, proyectando agregación sobre la unión de datos. Siri's Aquí es cómo funciona. Detrás de las escenas link puede operar en cualquier dato que implemente la interfaz I innumerable . Innumerables datos son básicamente una colección de artículos que puedes atravesar uno por uno. El I innumerables infecta contiene sólo un solo método llamado Gets in Numerador, por ejemplo, odiar en numerador para pasar por los datos. El in operator objects también tiene su propia interfaz. Frío I en numerador. Contiene sólo tres miembros. Pasar a continuación para pasar al siguiente ítem de la serie actual para recuperar el ítem actual, Tía Reset para saltar de nuevo al principio del Siri el final El numerador implementa una especie off hacia adelante solo maldice todo por recuperar los ítems en el Siri uno por uno . Cuando se utiliza el para cada declaración, El compilador crea encendido en M aireador detrás de las escenas para pasar a través de todos los elementos en el Siri uno por uno. Lo bonito de Link es que puede apilar operaciones en la parte superior, a menudo en el operador, sin ejecutarlas realmente. Considera el siguiente código que esta expresión prepara. Organizar números de 500 probabilidades, pero en realidad no genera los números todavía. Todo lo que hace es crear en en numerador Andi. Añádele una expresión de filtro al pasar por el rango. Por ejemplo, mediante el uso de la para cada instrucción. El aireador N M utiliza el movimiento. Siguiente mensaje. Tía aplica la expresión de filtro para devolver los valores correctos. Otra escena interesante a recordar es que el rango no ocupa ningún recuerdo. El operador in, Onley, rastrea el número actual. Andi. Tiene una expresión de filtro para calcular el siguiente número en el rango, por lo que la huella de memoria total fuera del operador in es solo del tamaño de un solo Eisen. A pesar de que el rango describe 500 ítems, puedes usar link para procesar cantidades muy grandes de datos mientras solo usas unas pequeñas cantidades fuera puedes usar link para procesar cantidades muy grandes de datos mientras solo usas unas pequeñas cantidades fuerade la memoria de la cadera porque el numerador solo rastreará el ítem actual. Pero el vínculo puede comportarse de formas inesperadas. Considera el siguiente programa. Escribí un corrector ortográfico en C. Sharp. Se puede ver que este proyecto se llama corrector ortográfico malo porque se asigna liberalmente . memoria en el montón en produce unas enormes huellas de memoria. Echemos un vistazo. Empezaré con este método de archivo de lectura. Aquí se abre un archivo de texto lo lee línea por línea sobre utiliza las sentencias de retorno de rendimiento para devolver cada línea como un nuevo elemento en una secuencia innumerable fuera de cadenas. En efecto, todos los métodos se convierten en un enorme cráter. Regresa los estados de retorno de rendimiento. El valor actual en el método move next avanzará el bucle while a la siguiente línea En el archivo aquí abajo está el mensaje principal del programa. Leí el archivo todas las palabras, que contiene un diccionario de aproximadamente 150 miles, palabras correctamente escritas. Después cargo otro archivo llama historia, que es la primera línea fuera de la victima PD. Un artículo sobre el país frente a España. Entonces llamo al hechizo Métodos de comprobación, que se declara aquí arriba. Spellcheck utiliza una consulta de enlace para dar un paso a través de cada palabra de la historia. Por cada palabra genero la versión en minúscula de las palabras y luego proyecto una nueva a manzana, consistente en las palabras originales encendidas y un valor booleano, indicando si las palabras podrían encontrarse nuevamente en el diccionario. Esta línea de código no genera la secuencia. Es simplemente construir un numerador muy complicado o ejecutar un hechizo. Consulta cada palabra en la historia. Por fin se está generando la secuencia aquí en el para cada enunciados. Paso por cada alumno en los resultados. Establezca el dedo del pie del color de la consola ya sea verde o rojo, dependiendo si la palabra se encontró en el diccionario o no, y luego muestre las palabras en la consola, más un espacio al final. Por lo que este programa debería mostrar toda la historia. Palabras por palabras Fans resalta cualquier error ortográfico en rojo. Ejecutemos el programa para comprobar si todo funciona, igual que en conferencias anteriores, voy a generar un lanzamiento build Andi. Entonces pasaré a la tía consola. Ejecuto el programa en la línea de comandos con el perfilador Log, para que así podamos ver todos los objetos que se asignan en el montón. Aquí vamos. Se puede ver que el rendimiento de este código no es genial. Esto es como una palabra par de segundos más o menos. A este ritmo, tardaría una eternidad en revisar todo el artículo de Wikipedia. Entonces supongo que tengo suerte de que sólo esté revisando la primera frase. De acuerdo, entonces esperemos hasta que se complete el programa. Aquí los resultados y no son buenos. El recolector de basura realiza 2.6 millones de movimientos de objetos mientras compacta el había 24 colecciones cero de generación en colecciones de 12 generación uno. En el resumen de asignaciones, podemos ver que tengo 2.7 millones de cadenas en el Heeb ocupando 128 megabytes. También tengo 349 arrays de cadenas ocupando otros 78 megabytes en total. La memoria del montón asignada es de más de 200 megabytes en el método llamado resumen muestra que el tiempo total de ejecución del programa es de 38 puntos cinco segundos. ¿ Qué pasa aquí? Déjame mostrarte que no te equivocaste aquí. Voy a cambiar de nuevo a Mona desarrollada Ahora mira esto para enumerar método aquí. Necesito el mensaje porque tengo que comprobar si las palabras de la historia aparecen en el diccionario. Hago eso con el método contiene. Pero para llamar a eso métodos primero necesito convertir las palabras del diccionario en admiración en una lista que Aiken busca. Ahora aquí está el problema. El diccionario variable en sí está encendido en M aireador Que carga la lista de palabras bajo demanda. Entonces para cada palabra en la historia, este código va a recargar toda la lista de palabras en convertida en una lista sólo para comprobar esa sola palabra. Tengo 18 palabras en mi historia, así que termino teniendo todo el diccionario en memoria 18 veces. Hay 145,000 palabras en el diccionario, así que eso me deja con 2.7 millones de cuerdas. Ocupando 128 megabytes. El listado del diccionario utiliza en matriz de agitación interna para el almacenamiento. El diccionario requiere ligeramente más de un megabytes fuera de almacenamiento, por lo que la lista habrá crecido a dos megabytes de capacidad en todas las matrices de cadenas descartadas anteriores suman otros dos megabytes, por lo que cada diccionario ocupa cuatro megabytes fuera de su memoria encendida. Ya que tengo 18 off ellos fizz como hasta 72 megabytes off heat memory. Juntos, esto suma hasta ocho, impresionante a cientos de megabytes de memoria de calor, Un desperdicio masivo fuera de la memoria en esto es lo lamentable del que estaba hablando. No es inmediatamente obvio del diccionario valioso que en realidad está encendido en operador que carga todo el diccionario en el montículo. El corrector ortográfico frío parece razonable, pero sólo si el diccionario en realidad se cobra completamente en memoria, por lo que podemos consultarlo rápidamente. Y este no es el caso aquí. Cuando estás escribiendo consultas de enlace complejas, es muy fácil de perder. Rastrea la implementación de los enumeradores que usas. ¿ Qué tan compleja es la implementación fuera del operador en? ¿ Cuántos artículos expone? Necesitarás esta información antes de poder decidir cómo hacerlo. Claramente la secuencia efectivamente enlace implementa una capa muy abstracta claramente en la parte superior de C capa afilada, y a veces la implementación de los innumerables datos subyacentes puede volver a morderte de formas inesperadas. La buena noticia es que no es difícil arreglar el corrector ortográfico. Hice esto en otro proyecto llamado Buen corrector ortográfico. Echemos un vistazo. El buen corrector ortográfico es muy similar al malo, pero una diferencia notable es que declaro el diccionario como una lista genérica fuera de cadenas aquí arriba este cambio elimina la necesidad de los métodos de lista. Llama al abrigo de corrector ortográfico. Tenga en cuenta también que inicializo la lista. 250,000 artículos. Esto evita que la lista se duplique en tamaño mientras agrega elementos. Andi elimina todas esas matrices de cadenas d referenciadas en el montón. El mensaje de revisión ortográfica es casi exactamente el mismo que el abrigo malo. La única diferencia es que ahora puedo llamar a los métodos contiene directamente en la variable diccionario porque ya es una lista genética. El cambio final está en el mensaje principal del programa aquí abajo. Ahora inicializo el diccionario usando esto para cada enunciado. De acuerdo, vamos a ver cómo coincide esta versión. Estoy creando una compilación de liberación cambiando a la línea de comandos al ejecutar el programa usando el perfilador de registro. Aquí vamos. Y aquí están los resultados. Estamos abajo a 145 mil movimientos de objetos. Una generación cero colección en a generación uno colecciones mucho mejor. El montón ahora contiene 145,000 cuerdas. Este es nuestro diccionario que ocupa 6.8 megabytes de memoria del montón. También tengo nueve arrays de cadenas que ocupan 1.2 megabytes. Esto es exactamente lo que esperarías. Un array de cadenas con 145,000 elementos cada uno en referencia de memoria de pila de ocho byte ocupará un poco más de un megabytes de él memoria exactamente lo que estamos viendo aquí. Por lo que esta implementación sólo asignará la palabra diccionario en el montón en nada más. Esto reduce las huellas de memoria a ocho megabytes, una enorme mejora de 25 veces. Entonces, ¿qué hemos aprendido? Link es un poderoso marco para ejecutar consultas sobre innumerables datos llamando a los métodos de lista en un enlace, expresión puede inflar inesperadamente las huellas de memoria fuera de tu abrigo para solucionar este problema. Enfriar para listar antes de ejecutar la consulta de enlace En mis códigos corrector ortográfico pre inicializar el diccionario de palabras resultó en una huella de memoria 25 veces menor. 17. recap de cursos: felicitaciones. Has completado todo el curso. Ahora eres un optimizador de memoria C sharp certificado. Te he mostrado ¿Cómo funciona Recolección de basura Nets? ¿ Cómo optimizar tus propios códigos para la recolección de basura? Andi Demostré varios trucos sencillos para mejorar drásticamente las huellas de memoria fuera tu abrigo. Echamos un vistazo detallado a la recolección de basura, identificamos los supuestos que hace el recolector de basura sobre el tamaño de los objetos en Lifetime e investigamos cómo puedes optimizar tu propio código para trabajar con estos supuestos, también cubrimos la memoria optimización. Medimos los efectos de varios trucos simples donde un diminuto cambio de código resultó en unas enormes mejoras en las huellas de memoria frente a la costa. Las habilidades que aprendes te han dado una rica caja de herramientas de conocimientos e ideas que puedes usar al escribir tus propios códigos o al colaborar en un equipo de desarrollo, sobre todo cuando estás trabajando en Misión fría crítica, donde baja el uso de la memoria y el rápido rendimiento es crucial. Si descubres algunas ideas interesantes por tu cuenta, por favor compártelas en la discusión del curso para él, para que todos disfrutemos. Adiós. Espero que nos volvamos a encontrar en otro curso