0

I know that there are lots of questions on this topic but my question is specifically: What happens if I allocate memory to say a buffer, and then I will never allocate memory in my code again. Do I then need to free that memory?

My application of the code is going to be for embedded design, so a microcontroller. The code would look something like this:

#include "ringbuffer.h"

const int BUFFER_SIZE = 5;       

// create instance of ring buffer
ringbuffer buff1;
ringbuffer buff2;

int main(void)
{
    // initialize ring buffer (this allocates memory to buffers)
    ringbuffer_init(BUFFER_SIZE, &buff1);
    ringbuffer_init(BUFFER_SIZE, &buff2);

    while(true)
   {
       // receive data in buffer here
       // do stuff with data
       // run forever
   }

    // memory is never freed for the buffers
    return 0;
}

The allocate memory to the buffer happens inside the ringbuffer_init() function, and this function would be permitted to only run once in the start of main, before the loop.

My question is really. If I allocate this memory to the buffer, never free it, and I turn off and on my microcontroller, will it allocate the memory twice so that over time it builds up and crashes?

.c and .h files for the ringbuffer library on GitHub.

C. K.
  • 71
  • 1
  • 11
  • 2
    With embedded code on a microcontroller it is better to allocate memory for a buffer that you will never free, statically. You know the buffer size, and there is no reason not to. Here the `struct` member would be an array, or a pointer to a static array. – Weather Vane Aug 13 '19 at 12:28
  • @WeatherVane I was trying to implement this! The reason for my choice of dynamically allocating memory for the buffer, is that I will have to decide the buffer size in the main code, while the buffer is defined in the library files. So to achieve a "user defined" buffer size I needed to use dynamically allocated memory. – C. K. Aug 13 '19 at 12:32
  • The code has `#define BUFF_SIZE` so you can still allocate a buffer statically, and place its pointer in the `struct`. But this isn't your code, because `#define BUFF_SIZE` contains no value. – Weather Vane Aug 13 '19 at 12:32
  • @WeatherVane you are right, I updated the code for clarity. – C. K. Aug 13 '19 at 12:37
  • 4
    @MariusGulbrandsen IMO it depends entirely on the "OS" used in your microcontroller. – Jabberwocky Aug 13 '19 at 12:38
  • 1
    It would be way easier if you would specify what is the "embedded" platform you are working on. This is dependend on your OS implementation (and sbrk, malloc and startup implementation) so I think this is too broad. If `sbrk` pointer is reset during module initialization, then it is. What if you OS chooses to store the memory in some non-volatile external memory that survives on/off (would be strange). Specify your standard library implementation. And anyway modern microcontrollers have POR, when power is inserted they start with everything zeroed. – KamilCuk Aug 13 '19 at 12:39
  • @WeatherVane This cleared up a lot actually. In [this](https://stackoverflow.com/a/57437945/11042379) answer to my previous question I was adviced to use dynamic allocating of memory, which worked. I tried now to do the exact same thing, but not using dynamic allocating for the *buff but just setting the size sent to it, and it worked! That actually seems to solve my problems then. – C. K. Aug 13 '19 at 12:49
  • Good: the previous question did not mention "embedded." – Weather Vane Aug 13 '19 at 12:51
  • @KamilCuk I'm working with Nordic SDK for an ARM based Bluetooth module [nRF52840](https://www.nordicsemi.com/Products/Low-power-short-range-wireless/nRF52840). It's all written in C with Nordic's stack. I'm not sure what "OS" that is, but I guess it's the Nordic stack or the ARM "OS"? – C. K. Aug 13 '19 at 12:51
  • @WeatherVane I guess it's hard to ask good questions. Thanks. – C. K. Aug 13 '19 at 12:52
  • 2
    In embedded environments you don't want to use dynamic allocated memory for various reasons. The easiest solution would be to staticly define the buffer and size in your main application and pass a pointer to the buffer to your lib, that is working on it. The only thing you have to make sure, is not to access the buffer outside of your lib. – grml Aug 13 '19 at 13:15

4 Answers4

3

What happens if I allocate memory to say a buffer, and then I will never allocate memory in my code again. Do I then need to free that memory?

  • On a hosted (PC-like) systems, you need to free the memory when you are done using it. The OS will reclaim it, but you should free it still. Partially because it's good practice to clean up your own mess, partially because when doing so, all heap-related bugs in your program will surface early on. When you get a program crash upon calling free(), the actual bug is elsewhere in the program.

  • On freestanding (microcontroller/RTOS) systems, you shouldn't use dynamic allocation in the first place, because it doesn't make any sense. If you for reasons unknown still insist on using it, you don't need to free() it because your program controls 100% of the available resources in the computer.

My application of the code is going to be for embedded design, so a microcontroller.

You shouldn't use that Github project in embedded systems. You need to get rid of malloc and replace it with a statically allocated buffer, one per instance of the ring buffer.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I basically agree with you, beside a small part. Using `malloc` on a microcontroller, will allocate memory in the heap section (that is in most cases a very small free-floating part of the memory). Not calling `free()` will let you run out of space there very fast. And relying on a restart (power cycle, watchdog, softreset) to take care of freeing memory is not good practice. – grml Aug 13 '19 at 13:55
  • @grml The OP is not concerned about freeing memory during run-time but when the application is done. If the programmer is aimlessly calling malloc over and over on a MCU, then the programmer is the problem. – Lundin Aug 13 '19 at 14:19
  • This makes my understanding of memory management and programming of embedded systems much clearer. Thanks. Regarding the GitHub project, it's actually mine. So as mentioned in a comment on my [question](https://stackoverflow.com/q/57477689/11042379) I found a way to statically allocate the memory after all and will thus update it:) – C. K. Aug 13 '19 at 21:08
  • 1
    @MariusGulbrandsen also, dynamic memory allocation needs to spend precious bytes for bookkeeping. – Antti Haapala -- Слава Україні Aug 14 '19 at 08:50
  • @MariusGulbrandsen loooking at that GitHub implementation of yours, you already have a memory leak, all the time you use ringbuffer_put(), once already after ringbuffer_init() and then always after ringbuffer_get() when the ringbuffer is empty. – kesselhaus Aug 15 '19 at 02:33
2

If I allocate this memory to the buffer, never free it, and I turn off and on my microcontroller, will it allocate the memory twice so that over time it builds up and crashes?

No, When you end the program the memory is generally reclaimed by the OS.

In your case, you have an embedded system and do not end the program. What will usually happen is that when you restart the system, the start up code will allocate the memory regions again and you will allocate the memory only once. There is no guarantee that the same address is allocated every time though.

That being said, It is a good practice to free the memory when you are done with it. This is because as your programs become bigger and you write code that goes into other systems, it becomes important to free the memory at the end of your part of the program.

Rishikesh Raje
  • 8,556
  • 2
  • 16
  • 31
  • Thank you for this. It makes things clearer. So I guess the OS will generally take care of this on the next startup for something like an embedded design. But of course, good practice to free the memory. – C. K. Aug 13 '19 at 12:54
  • 1
    @MariusGulbrandsen Typically, applications in a microcontroller are not loaded and unloaded dynamically, and do not terminate - restarting typically means a power-on reset. Most often there is no OS. Where there is an OS with load/unload and memory management responsibility, then the situation is no longer specific to microcontrollers/embedded systems. Memory allocation is not persistent over a power cycle/system restart. – Clifford Aug 14 '19 at 13:05
1

The normal rule on an embedded microcontroller is to never use dynamic memory allocation because in the limited memory space available to a microcontroller you can, over time, get memory space fragmentation and end up in a situation where the memory manager cannot find a large enough free block of memory to complete the memory allocation.

There are two cases that I can think of where allocating memory off the heap could be permitted

  • When all allocations are done at startup and are never freed (your situation)
  • When all allocations are made with the same block size.

In the first case any failures in allocation will happen before the main code starts and there is no possibility of an allocation failure happening after a long time running as there are never any more allocation attempts.

In the second case, assuming that the maximum number of allocations can be supported by the available memory, the memory cannot become fragmented as any new allocation will always fit into a block that has been freed.

In your case you will not have to free the memory as your application continues to run for as long as there is power available. When power is removed the memory will inevitably be freed.

uɐɪ
  • 2,540
  • 1
  • 20
  • 23
  • In either of these cases, you gain nothing but extra code bloat from calling malloc instead of just declaring the variable with static allocation. There should be no reason why you'd want to needlessly slow down the program and consume extra program flash for storing malloc. You'll have to be able to justify the use of malloc, which simply isn't possible to do. – Lundin Aug 14 '19 at 08:54
  • Granted but some frameworks e.g. FreeRTOS use malloc to allocate the task stacks etc when the tasks are created. If all of the tasks are started and then left running without any task desruction and re-creation then this will not be a problem. – uɐɪ Aug 14 '19 at 10:33
  • Since there exists no system with infinite RAM, there must always be a maximum limit of how many stacks you can allocate simultaneously. The whole of the program must still function up to that limit. Thus common sense dictates that the RTOS only needs to allocate as many stacks as is required for that worst case. – Lundin Aug 14 '19 at 10:40
  • That is why my answer says that all allocations are made at the start of the run so that the memory allocation becomes a static set of allocated areas on the heap. If you do not have enough memory to allocate the task stacks it will fail at initialisation and you will not get failures due to memory allocation later in the program run. – uɐɪ Aug 14 '19 at 10:48
  • That is not the only argument for not using dynamic memory, and may not even apply - not all embedded systems are memory constrained. A less obvious issue is that the non-deterministic allocation time, and potential for failure makes it unsuited to meeting hard real-time deadlines. Unlike @Lundin, I would not say never, but you do need to understand the issues to determine if it is appropriate or safe, and when you do, will often choose to avoid. If you do not understand the issues, then prohibition is a good guideline. – Clifford Aug 14 '19 at 12:53
0

If you power off and reboot your controller any memory should be lost. The startup code should initialize the whole memory with 0 and the OS should be able to allocate the same memory for the buffers again.

If it is in general a good idea to use malloc on an embedded system depends mainly on the used operating system. Most embedded OS' use definable numbers of different fixed sized mem blocks and allocate them depending on requested size as needed. They mostly supports some kind of statistics so you can watch the use of the blocks.

Don't create large buffers as automatic variables in functions. They can blast the stack. Be aware that declaring static vars as buffer may consume FLASH/ROM memory too for the init section. (Static vars were initialized per default.)

toto-w
  • 49
  • 5