Saltar al contenido

¿Por qué se considera una mala práctica utilizar la palabra clave ‘nueva’ en Arduino?

Te damos la bienvenida a nuestro sitio web, ahora hallarás la respuesta de lo que buscabas.

Solución:

La mayoría de los Arduinos (como Uno o Nano) tienen muy poca RAM, por lo que primero debe asegurarse de no asignar demasiada memoria. Además, la asignación dinámica de memoria puede conducir a la fragmentación del montón (el montón es la parte de la memoria, donde ocurre la asignación dinámica).

En la mayoría de los casos, querrá asignar memoria de diferentes tamaños (por ejemplo, matrices de diferentes tamaños) o simplemente objetos diferentes (cada uno con su propio tamaño) (!!! Este es el key apunte aquí). Luego, eliminará algunos de estos objetos. Eso creará agujeros dentro de la memoria. Se pueden volver a rellenar con objetos del mismo o menor tamaño. A medida que pasa el tiempo y se realizan más asignaciones y eliminaciones, estos agujeros tienden a hacerse más pequeños, hasta el punto en que ninguno de sus nuevos objetos para asignar puede caber allí. Entonces ese recuerdo es inutilizable. Este fenómeno se denomina fragmentación del montón.

Estos agujeros aparecen de forma natural, también en una PC. Pero hay 2 key diferencias:

  1. El Arduino tiene tan poca RAM que los agujeros pueden llenar tu memoria muy, muy rápido.

  2. Si bien la PC tiene un sistema operativo, que administra la RAM (desfragmentando o guardando las cosas no utilizadas en un archivo de paginación / intercambio), el Arduino no tiene un sistema operativo. Así que nadie vigila la RAM real disponible y nadie ordena la memoria de vez en cuando.

Eso no significa que no pueda usar la asignación dinámica en un Arduino, pero eso es muy arriesgado dependiendo de lo que esté haciendo exactamente y cuánto tiempo debería funcionar el programa sin fallar.

Teniendo en cuenta esta gran advertencia, está muy limitado sobre cómo utilizar la asignación dinámica. Hacerlo demasiado resultará en un código muy inestable. Las posibilidades restantes, donde podría ser seguro usarlo, también se pueden hacer fácilmente con static asignación. Por ejemplo, tome su cola, que es básicamente una lista enlazada. ¿Dónde está el problema de asignar un array de QueueItems al principio. Cada elemento tiene una forma de determinar si es válido. Al crear un nuevo elemento, simplemente elija el primer elemento en el array, que tiene un elemento no válido y configúrelo en el valor deseado. Todavía puede usar los datos a través de los punteros, como antes. Pero ahora lo tienes con static asignación.

Puede encontrar que el código se ve más feo de esa manera, pero necesita adaptarse a la plataforma que usa.

Tenga en cuenta que esto no aplicar, cuando vas a crear solo objetos con el mismo tamaño. Entonces, cualquier objeto eliminado dejará un agujero, donde cualquier objeto nuevo puede caber. El compilador usa ese hecho. Entonces, en ese caso, estás a salvo. Cada objeto, que cree dinámicamente en su programa, debe tener exactamente el mismo tamaño. Eso, por supuesto, también incluye objetos, que se crean dentro de diferentes bibliotecas o clases. (Por esta razón, aún puede ser una mala elección de diseño, ya que usted u otros (si desea publicar su código) pueden querer emparejar su biblioteca con otro código)

Otra forma de estar seguro es crear y eliminar objetos solo en ciclos cerrados, lo que significa que un objeto creado debe eliminarse antes de que se cree el siguiente objeto. Aunque eso no es apropiado para su aplicación.


En microcontroladores más grandes, por ejemplo, las placas que no son Arduino con ESP32, tienen mucha más memoria. Por lo tanto, el uso de la asignación dinámica no es tan malo para ellos. Aunque todavía no tienes un sistema operativo para administrar la RAM.

La asignación dinámica generalmente se desaconseja en aplicaciones integradas porque no puede garantizar que no exceda (intente asignar más de) la memoria disponible. La asignación estática generalmente tendrá esta garantía, aunque es posible que aún sean posibles errores de memoria insuficiente.

Además, hay muchos menos servicios o herramientas disponibles para administrar automáticamente y cuidar la memoria por usted. Cualquier servicio que lo haga consumirá recursos computacionales.

Esto significa que inherentemente crea un mecanismo en su dispositivo que causaría un desbordamiento de memoria (montón) y un posible comportamiento indefinido (UB). Este es true incluso si su código está libre de errores y no tiene pérdidas de memoria.

En aplicaciones no críticas, de exploración, aprendizaje y prototipos, esto puede no ser importante.

Tenga en cuenta que sin una consideración cuidadosa, el comportamiento indefinido puede resultar en fallas de hardware y un rendimiento inseguro, por ejemplo, si el dispositivo reconfigura GPIO a través de una escritura errónea en los registros correctos durante un bloqueo.

Para empezar, arregla tu biblioteca

Como señaló @crasic, la asignación de memoria dinámica generalmente no se recomienda para sistemas integrados. Puede ser aceptable para dispositivos integrados que tienen una mayor cantidad de memoria libre; por ejemplo, Linux integrado se usa comúnmente, y todas las aplicaciones / servicios de Linux tenderán a usar la asignación de memoria dinámica, pero en dispositivos pequeños como un Arduino simplemente existe no hay garantía de que esto funcione.

Su biblioteca ilustra una razón común por la que esto es un problema. Tu enqueue() función crea un new QueueItem() pero no comprueba que la asignación se haya realizado correctamente. El resultado de una asignación fallida puede ser un C ++ bad_alloc excepción, o puede estar devolviendo una null puntero, que cuando haga referencia a él dará una excepción de acceso a la memoria del sistema (señal SIGSEGV en Linux, por ejemplo). Es casi universal en la programación de Linux y Windows ignorar las fallas de asignación de memoria (como lo recomiendan la mayoría de los libros de texto), porque la enorme cantidad de RAM libre y la existencia de memoria virtual hacen que esto sea muy poco probable, pero esto es inaceptable en la programación integrada.

Sin embargo, de manera más general, como dice @crasic, la fragmentación de la memoria puede dejar incluso el código sin errores incapaz de asignar memoria. El resultado será una falla en la asignación de memoria, pero el código al menos sabrá que esto sucedió y probablemente podrá continuar.

Pero mejor, use una cola FIFO de tamaño fijo en su lugar

Su código se basa en la asignación dinámica para agregar y eliminar elementos en una cola. Es perfectamente posible (e igualmente fácil de codificar) crear un tamaño fijo array para la cola, por lo que los diversos modos de falla de la asignación dinámica simplemente no se aplican. Un elemento que se va a poner en cola simplemente se copia en el siguiente espacio de cola libre, y un espacio de cola se marca como libre cuando se ha utilizado. (No olvide usar un mutex cuando agregue y elimine elementos de la cola, porque a menudo se llamará a agregar y eliminar desde diferentes lugares).

La cola se puede hacer del tamaño que considere apropiado (teniendo en cuenta la cantidad de RAM que tiene). Con un tamaño fijo, se ve obligado a tomar una decisión de diseño sobre lo que debería suceder si la cola se desborda: ¿elimina los datos más antiguos para dejar espacio para el nuevo valor o ignora el nuevo valor? Esto puede parecer una nueva característica no deseada, pero es una bien cosa, porque la tercera opción que tienes actualmente es que tu código diga “¡Aaaarrggghhh, no sé qué hacer!” y se estrella fatalmente, y realmente no queremos eso.

Si eres capaz, puedes dejar un post acerca de qué te ha gustado de esta reseña.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *