Memory usage with delete keyword in a sketch

Hi! I am experiencing strange behavior with my sketch. Well, I think I have a hunch but not 100% positive. The question is on delete keyword with dynamic memory allocations. I know dynamic memory allocations is not advisable but my project is not going to be how I want it to be without it. So, with that said question comes down to this: why does my memory usage increase if I just use delete keyword with the rest of the code being the same. Here is the simple sketch:

#include "src\core\free_memory.h"
class Test { };
Test* test;

void setup()
{
	Serial.begin(115200);

	// prints 8698
	Serial.println(freeMemory());

	test = new Test();

	// prints 7969 if I use delete below, 8698 if I comment out delete
	Serial.println(freeMemory());

 	delete test;
}

void loop() { }

Second print prints 7969 if I use delete and 8698 if I do not. So, ~700 bytes have been used for something.

The reason this happens I think is that when compiler sees 'delete' keyword it automatically allocates that memory. I guess delete needs it for something. I would be grateful if someone can point me in the right direction on this.

Also, would free() function help me to get back that memory?

Thank you all!

EDIT: I am using Arduino Mega 2560 for this project

Because the optimizer notices test has no side effects so it's removed. Once removed, everything related is removed which includes the overhead of managing a heap.

I'll leave this here for you to consider.

It's not delete that needs something.

In layman's terms from a self-taught programmer, if you don't use the delete, the compiler throws test away because it is not used, If you do use delete, the compiler knows test was used (in delete) so does not throw it away.

A better example would have been if you had a variable in your class (e.g. and int) and manipulate (e.g. in a for-loop increment it) and print it. Next you can test with and without delete.

1 Like

Thanks! Appreciate it!

What do you mean by "side effect"? Modifying private member of the class or a variable inside setup() function by calling a public function of the class does not seem to change behavior.

I am building LED strip controller that include various effects and sequences for different occasions - Christmas, Halloween, Valentines, Independence Day and etc. All of them include cross-fade and other types of transitions. LED strips would consist of ~500 LEDs. I am getting too close to the limit. Had to tune down some of my parameters to avoid crashing. So, optimizations are becoming critical.

Thanks! I thought of that after other post mentioned about side effects. Below is the code I tried with same behavior...

#include "src\core\free_memory.h"

class Test {
	public:
		int a = 0;
		int method() { a = 2; return 4; }
};

Test* test;

void setup()
{
	Serial.begin(115200);
	Serial.println(freeMemory());	
	test = new Test();
	int a;
	a = test->method();
	Serial.println(freeMemory());
	delete test;
}

void loop() { }

You need to do something useful with the variables in the class. This is a demo code for a Mega.


class Test
{
public:
  int a = 0;
  int method()
  {
    a = 2;
    return 4;
  }
};

Test* test;

void setup()
{
  Serial.begin(115200);

  Serial.println("Before allocation ");
  display_freeram();

  test = new Test();

  Serial.println("After allocation ");
  display_freeram();

  for (int i = 0; i < 10; i++)
  {
    test->a = i;
    Serial.println(test->a);
  }

  Serial.println("Before delete ");
  display_freeram();
  delete test;
  Serial.println("After delete ");
  display_freeram();
}

void loop() {}



void display_freeram()
{

  Serial.print(F("- SRAM left: "));

  Serial.println(freeRam());
}


int freeRam()
{

  extern int __heap_start, *__brkval;

  int v;

  return (int)&v - (__brkval == 0

                      ? (int)&__heap_start
                      : (int)__brkval);
}

The output

Before allocation 
- SRAM left: 7914
After allocation 
- SRAM left: 7910
0
1
2
3
4
5
6
7
8
9
Before delete 
- SRAM left: 7910
After delete 
- SRAM left: 7914
1 Like

So how does using dynamic allocation help? If you need the memory, you need the memory.

Why not implement your project on a platform that's not so memory limited?

Without going into details its mostly for crossfade transitions and coding style requirements. I am a .NET developer with some C++ experience. I challenged myself to create reusable, readable and maintainable code for this project. Similar to how we use design patterns and object construction in .NET.

I was thinking about it and may decide to go for that after all but cost for the board is concern for now. I want to use it in several places. Each of those would require its own board and total cost may add up. This is also the reason I wanted to create reusable code so I can share it between board implementations.

This now seems appropriate.

We are, after all, in the Programming Questions section of the forum and no project related code has been posted so far.

Really, not the Hand Waiving section?

To avoid confusion I did not want to include whole project and decided to include only a snipped that was relevant to the question. My project consists of 30+ .cpp and .h files. Unfortunately I am not able to upload files here and I am hesitant to make my github repository public. To provide an example at least, I will try to paste some code here but without project in whole they may not provide full picture or my "vision". :slight_smile:

// selects animation (factory that creates animation sequence) based on the pin jumpers
class PinAnimationFactory : public IAnimationFactory
{
	private:
		IAnimationFactory* factories[3] = { 
			new ThanksgivingAnimationFactory(), 
			new NewYearAnimationFactory(), 
			new HalloweenAnimationFactory() 
		};

	public:
		PinAnimationFactory()
		{
			pinMode(FIRST_PIN, INPUT_PULLUP);
			pinMode(SECOND_PIN, INPUT_PULLUP);
			pinMode(THIRD_PIN, INPUT_PULLUP);
		}

		IAnimation* create()
		{
			uint8_t val = (!digitalRead(FIRST_PIN) << 2) | (!digitalRead(SECOND_PIN) << 1) | !digitalRead(THIRD_PIN);
			Serial.print("Creating Animation: ");
			Serial.println(val);

			if (val >= 0 && val <= 1) {
				IAnimationFactory* f = factories[val];
				return f->create();
			}
			return new SolidColorAnimation(CRGB::White);
		};
};

// creates thanksgiving animation sequence
class ThanksgivingAnimationFactory : public IAnimationFactory
{
	private:

		const CRGB brightRed = CRGB(255,0,0);
		const CRGB lightRed = CRGB(40,25,35);
		const CRGB darkRed = CRGB(50, 0, 0);

		const CRGB brightOrange = CRGB(255,100,25);

		const CRGB white = CRGB(217,211,250);
		const CRGB gray = CRGB(90,90,90);
		const CRGB darkGray = CRGB(30,30,30);

		const CRGB brightGold= CRGB(255,200,25);
		const CRGB darkGold = CRGB(50,40,0);

		const CRGBPalette16 palette =
		{
			brightRed, gray, lightRed, gray,
			lightRed, gray, darkGold, gray, 
			brightOrange, gray, white, gray,
			brightGold, gray, darkGold, gray
		};

		IAnimationFactory* effects[5] = {
			(new TwinkleEffectFactory())->setColors(lightRed, brightOrange)->setTiming(250,2000,750,2000)->setFillPct(80),
			(new TwoColorFactory())->setColors(CRGB::DimGray, brightGold, brightOrange)->setTiming(100, 1000)->setColorSize(2, true),
			(new SparkleAnimationFactory())->setColors(darkGold, CRGB::Gray),
			(new TwinkleEffectFactory())->setColors(CRGB::DimGray, brightGold)->setTiming(750,1000,750,100)->setFillPct(80),
			(new PaletteWaveFactory())->setSpeed(200)->setPalette(palette)
		};

	public:
		IAnimation* create()
		{
			return new SequenceAnimation(effects, ARRAY_SIZE(effects), ANIMATION_DURATION_MS, ANIMATION_TRANSITION_MS);
		};
};

// SequenceAnimation handles sequence and cross fade transitions between effects
void SequenceAnimation::attach(LedStrip* strip)
{
	mainStrip = strip;
	currentAnimation = getNextFactory()->create();
	if (currentAnimation == NULL)
		return;

	currentStrip = mainStrip->createVirtual();
	currentAnimation->attach(currentStrip);
}

void SequenceAnimation::detach()
{
	if (currentAnimation != NULL) { currentAnimation->detach(); delete currentAnimation; currentAnimation = NULL; }
	if (nextAnimation != NULL) { nextAnimation->detach(); delete nextAnimation; nextAnimation = NULL; }

	if (currentStrip != NULL) { delete currentStrip; currentStrip = NULL; }
	if (nextStrip != NULL) 	{ delete nextStrip; nextStrip = NULL; }

	this->mainStrip = NULL;
}

void SequenceAnimation::draw()
{
	if (mainStrip == NULL || currentAnimation == NULL)
		return;

	unsigned long currentTime = millis();

	uint8_t currentIndex = (currentTime / animationLength) % 2;
	uint8_t newIndex = ((currentTime + crossFadeLength) / animationLength) % 2;
	
	if (currentIndex != newIndex)
	{
		if (nextAnimation == NULL)
		{
			nextAnimation = getNextFactory()->create();
			if (nextAnimation == NULL)
				return;

			nextStrip = mainStrip->createVirtual();
			nextAnimation->attach(nextStrip);
		}

		currentAnimation->draw();
		nextAnimation->draw();
		uint16_t transitionTime = (currentTime + crossFadeLength) % animationLength;
		fract8 amt = transitionTime/(crossFadeLength / (float)256);
		for(unsigned int i = 0; i < mainStrip->count; i++) {
			mainStrip->leds[i]=blend(currentStrip->leds[i], nextStrip->leds[i], amt);
		}
	}
	else
	{
		if (nextAnimation != NULL)
		{
			currentAnimation->detach();
			delete currentAnimation;
			delete currentStrip;

			currentAnimation = nextAnimation;
			currentStrip = nextStrip;

			nextAnimation = NULL;
			nextStrip = NULL;
			currentIndex = newIndex;
		}

		currentAnimation->draw();
		for(unsigned int i = 0; i < currentStrip->count; i++) {
			mainStrip->leds[i] = currentStrip->leds[i];
		}
	}
}

class AnimationBase : public IAnimation
{
	private:
		bool firstFrame = true;

	protected:
		LedStrip* strip;
		virtual void onDraw() = 0;
		virtual void onAttach() { }
		virtual void onDetach() { }
		virtual void onFirstFrame() { }

	public:
		void attach(LedStrip* strip) 
		{ 
			this->strip = strip; 
			firstFrame = true;
			onAttach();
		}

		void detach() 
		{ 
			onDetach();
			this->strip = NULL; 
		}

		void draw()
		{
			if (!strip)
				return;

			if (firstFrame)
				onFirstFrame();
			onDraw();
			firstFrame = false;
		}

		virtual ~AnimationBase()
		{
		}
};

// effect that simply moves palette along the LED strip configurable with constructor parameters
class PaletteWaveAnimation : public AnimationBase
{
	private:
		CRGBPalette16 palette;
		unsigned int startIndex;
		TBlendType blendType;
		uint8_t speed = 200;

	public:
		PaletteWaveAnimation(CRGBPalette16 palette, unsigned int speed, TBlendType blendType)
		{
			this->palette = palette;
			this->blendType = blendType;
			this->speed = speed;
			startIndex = 0;
			Serial.println("Create Palette Wave Effect");
		}

		void onAttach() override
		{
			startIndex = 0;
		}

		void onDraw()
		{
			uint8_t delay = 255 - speed;
			EVERY_N_MILLISECONDS(delay) { 
				drawLeds();
			};	
		}

		void drawLeds()
		{
			int colorIndex = startIndex;
			for( int i = 0; i < strip->count; i++) {
				CRGB color = ColorFromPalette(palette, colorIndex, 255, blendType);
				strip->leds[i] = color;
				colorIndex += 3;
			}
			startIndex++;
		}
};

My vision or aim really is to be able to setup animations quickly and clearly, using some kind of method chaining maybe and short of using video editor :laughing: . For example in pseudocode:

IAnimation* timeline = createTimeline()
.play(SparkleEffect(color1, color2), 60sec)
.transition(CrossFadeTransition(), 1000ms)
.play(RainbowPaletteEffect(), 30seconds)
.transition(DissolveTransition(), 500ms)
.play(SparkleEffect(color3, color4), 30sec)
.repeat(3times))
.play(createSomeOtherComplexTimeline())
//and so on and so forth

This would allow me to mix and match animations for different scenarios or occasions without worrying too much about inner workings. But I guess when I get to this point Arduino Mega will be stretched too thin. :smiley:

F-macro. (I cannot find it in the online documentation or I would have linked to that.)

1 Like

It is generally a bad idea to touch hardware in constructors.

While I'm generally a fan of factories and virtual construction, VMTs cost SRAM. Given the fact that you have just three factories and they are burned into the uploaded image, virtual construction gains you little but costs precious memory.

Makes it impossible to construct a HalloweenAnimationFactory.

1 Like

I was trying to use that some time ago but had some issues at the time. Seems to be compiling now. Will look into it again I guess. But that would not give me much I think, would it?

What is the reason? To avoid accidentally call pinMode if you instantiate multiple time or some other reason? Anything can be done to kind of encapsulate pin use within the class? Static method on the class and call it from setup() explicitly?

Yeah, my bad. Thanks! Incomplete code - bug :smiley:. Planning to have up to 8 factories controllable by 3 jumpers but most likely will be considering more than that in the near future.

The hardware is not fully initialized until setup is called. Presumably things like PinAnimationFactory are global scope which means they're initialized before setup is called.

You could check ESP32-S3, S2 or C3. You can have up to 16MB and they cost about 5€. It has less pins maybe, but you can solve that in different ways.
In my option the Arduino boards, in general, are good for educational purposes or quick prototypes with jumpers, but not for something more permanent. The same for the Arduino IDE, when the code starts to grow.
Of course you can put a rocket in Mars with that, but is not best the best option, in my opinion.

With ESP32 you have also most of the STD libraries, and C++11 or newer.
And you can also write good and reusable code without extensive OOP and strict patterns, an MCU is not a server, different things are more critical. I suppose that something in between depending on your preferences.