Page 1 of 1

Why each instance of my class consume 20byte of memory at least?

Posted: Wed Aug 15, 2018 9:17 am
by human890209
Hi,
I'm a newbie of Arduino-esp32, I used to use Arduino.
I succeed to run the blink example on my ESP32-WROOM-32 dev module.
And I began to test other functions.

I make a simple RAM test to figure out the difference in RAM usage between ESP32 and Arduino.
I found creating a new instance of a class will consume 20byte of RAM at least.
-----
An empty class takes 20byte.
A class with a 1byte variable takes 20byte.
A class with a uint16_t variable takes 20byte.
A class with a uint32_t variable takes 20byte.
A class with 3 uint32_t variables takes 28byte.
-----
It seems that a Class will at least consume 20byte no matter what it contains.
Here is my simple testing sketch:

Code: Select all


#define LEDPIN	2

bool isOn = true;

// TestClass
class myClass
{
public:
	myClass(){};
	~myClass(){};
	//uint8_t content = 0;
	//uint16_t content = 0;
	//uint32_t content = 0;
};

// GetFreeRAM
#ifdef ESP32
	// nothing
#else
	#ifdef __arm__
		// should use uinstd.h to define sbrk but Due causes a conflict
		extern "C" char* sbrk(int incr);
	#else  // __ARM__
		extern char *__brkval;
	#endif  // __arm__
#endif // ESP32

int getFreeRam()
{
#ifdef ESP32
	return (int)ESP.getFreeHeap();
#else
	char top;
	#ifdef __arm__
		return (int)(&top - reinterpret_cast<char*>(sbrk(0)));
	#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
		return &top - __brkval;
	#else  // __arm__
		return __brkval ? &top - __brkval : &top - __malloc_heap_start;
	#endif  // __arm__
#endif //ESP32
}

uint32_t count;
uint32_t count_max = 60000;
myClass **classArray;


void setup()
{
	Serial.begin(115200);
	pinMode(LEDPIN,OUTPUT);
	Serial.println(F("Setup"));
	count = 1;
}


void loop()
{
	// Check count max
	if (count > count_max)
	{
		count = count_max;
	}

	// Report count
	Serial.print("Count: ");
	Serial.println(count);

	// Report Basic Size
	Serial.print("sizeof(myClass*): ");
	Serial.println(sizeof(myClass*));
	Serial.print("sizeof(myClass): ");
	Serial.println(sizeof(myClass));


	// Create the class array
	classArray = (myClass**)malloc(count * sizeof(myClass*));
	for (uint32_t i = 0; i < count; i++)
	{
		classArray[i] = new myClass();
	}

	// Report FreeRam
	Serial.print("CreateFreeRam: ");
	Serial.println(getFreeRam());
	digitalWrite(LEDPIN, isOn);
	isOn = !isOn;


	// Clear the class array
	for (uint32_t i = 0; i < count; i++)
	{
		delete(classArray[i]);
	}
	free(classArray);

	// Report FreeRam
	Serial.print("ResetFreeRam: ");
	Serial.println(getFreeRam());

	// Loop Delay
	delay(50);
	count+=1;
}
Here is the log of serial monitor

Code: Select all

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:952
load:0x40078000,len:6084
load:0x40080000,len:7936
entry 0x40080310
Setup
Count: 1
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 292048
ResetFreeRam: 292088
Count: 2
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 292024
ResetFreeRam: 292088
Count: 3
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 292000
ResetFreeRam: 292088
Count: 4
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291976
ResetFreeRam: 292088
Count: 5
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291952
ResetFreeRam: 292088
Count: 6
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291928
ResetFreeRam: 292088
Count: 7
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291904
ResetFreeRam: 292088
Count: 8
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291880
ResetFreeRam: 292088
Count: 9
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291856
ResetFreeRam: 292088
Count: 10
sizeof(myClass*): 4
sizeof(myClass): 4
CreateFreeRam: 291832
ResetFreeRam: 292088
Hope someone could explain why an instance of an empty class will take 20byte.
And is there a way to make it smaller?

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Wed Aug 15, 2018 1:47 pm
by kolban
Its an interesting question ... (at least to me). I'm tempted to say that this is simply housekeeping. I think the detailed answer will come from examining a number of areas. The first will be memory management. We should assume that a class instance is carved out of the heap. This then begs the question if I allocate X bytes on the heap, is the heap reduced by X bytes only? Pretty obviously it can't be less than X bytes but how much (if any) additional will be allocated? The answer will be up to the heap management algorithms. When you allocate X bytes, you are returned an address and for the next X bytes they are yours ... however this says nothing about bytes preceding X or following X. For example, an allocation of memory could easily (behind the scenes) place bookkeeping or house keeping data. For example, some number of bytes before and after your X bytes could be over-run/under-run markers which are expected to have constant values. If your code goes bad and steps on these we can detect that. This would result in a memory usage > X bytes.

If class instance allocation is created using the heap, then each instance of a class may have this housekeeping.

Can a class instance be allocated in zero bytes if it has no state? I think not as an instance of a class is itself state. If I create two instances of a class (even if they have no state) they are not the same instance. So there must be a minimum class instance size.

See also:

https://stackoverflow.com/questions/236 ... c-not-zero

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Thu Aug 16, 2018 1:00 am
by human890209
Hi, kolban.
Thanks for your reply. I guess there are some "margin" or "boundary" of a class instance in memory.
Cause I tested this sketch on Arduino micro and stm32duino, Arduino micro consumes extra 2 bytes and stm32duino consumes a pattern of 12 bytes 12 bytes 24 bytes...
I guess 32bit MCU did need more memory to mark the margin/boundary, just like it's pointer needs 4bytes which is bigger than the 8bit' s one.

And I modified my sketch like this to test if a nested class will consume more memory

Code: Select all


#define LEDPIN	PC13

bool isOn = true;

// TestClass
class myClass2
{
public:
	myClass2(){};
	~myClass2(){};
	//uint8_t content = 0;
	uint16_t content = 0;
	//uint32_t content = 0;
};

class myClass
{
public:
	myClass(){};
	~myClass(){};
	//uint8_t content = 0;
	//uint16_t content = 0;
	//uint32_t content = 0;
	myClass2 content;
};


// GetFreeRAM
#ifdef ESP32
	// nothing
#else
	#ifdef __arm__
		// should use uinstd.h to define sbrk but Due causes a conflict
		extern "C" char* sbrk(int incr);
	#else  // __ARM__
		extern char *__brkval;
	#endif  // __arm__
#endif // ESP32

int getFreeRam()
{
#ifdef ESP32
	return (int)ESP.getFreeHeap();
#else
	char top;
	#ifdef __arm__
		return (int)(&top - reinterpret_cast<char*>(sbrk(0)));
	#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
		return &top - __brkval;
	#else  // __arm__
		return __brkval ? &top - __brkval : &top - __malloc_heap_start;
	#endif  // __arm__
#endif //ESP32
}

uint32_t count;
uint32_t count_max = 1000;
myClass **classArray;


void setup()
{
	Serial.begin(115200);
	pinMode(LEDPIN,OUTPUT);
	Serial.println(F("Setup"));
	count = 1;
}


void loop()
{
	// Check count max
	if (count >= count_max)
	{
		count = count_max;
		// SoftwareReset Test
		#ifdef ESP32
			ESP.restart();
		#endif
		return;
	}

	// Report count
	Serial.print("Count: ");
	Serial.println(count);

	// Report Basic Size
	Serial.print("sizeof(myClass*): ");
	Serial.println(sizeof(myClass*));
	Serial.print("sizeof(myClass): ");
	Serial.println(sizeof(myClass));


	// Create the class array
	classArray = (myClass**)malloc(count * sizeof(myClass*));
	for (uint32_t i = 0; i < count; i++)
	{
		classArray[i] = new myClass();
	}

	// Report FreeRam
	Serial.print("CreateFreeRam: ");
	Serial.println(getFreeRam());
	digitalWrite(LEDPIN, isOn);
	isOn = !isOn;


	// Clear the class array
	for (uint32_t i = 0; i < count; i++)
	{
		delete(classArray[i]);
	}
	free(classArray);

	// Report FreeRam
	Serial.print("ResetFreeRam: ");
	Serial.println(getFreeRam());

	// Loop Delay
	delay(50);
	count+=1;
}
The result is the same. So it's a memory boundary to prevent memory war. ;)
I think I should live with that. But it also reminds me that the SRAM size is not 100% efficient. ESP32's 292kbyte(getFreeHeap()'s result) SRAM is not 146x of Arduino Pro mini 2kb. If use pieces of small instances like my project. It could waste a lot of memory in creating "margins" or "boundaries".

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Thu Aug 16, 2018 2:38 pm
by human890209
Hi,
I test the sketch on ESP8266.
At first, I'm very happy that it only adds 2~4 bytes memory margin to the instance.
But when the instance number increase, the margin is getting bigger. At the end of the free heap, it could get 12 bytes (6x of the 1st one) for an instance.
And my ESP8266's free heap is only 47 kbytes. I can't imagine how big the margin will get for the 292 kbytes ESP32.
So I decide to test ESP32 and my other boards again to see if the margin is also dynamically increasing.
But the test seems to inspire me that a big SRAM is not good for my project, cause the 2nd half's efficiency may be far lower than the 1st half.
The housekeeping cost will be far more than the instance itself.

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Thu Aug 16, 2018 3:59 pm
by kolban
If I may ask? How many instances of classes are you looking to create? What is the "nature" of the class? What kind of state data might it contain?

With embedded systems, we obviously have less resources available to us than desktop or rack machines. This may mean we may have to trade programming convenience and elegance for pragmatic reasons. If, for example, your unit of data per class were only a few bytes (for example fields int a and int b) then you might find that creating a:

struct myData {
int a;
int b;
}

and then allocating from the heap the maximum number of such entities you will need:

malloc(sizeof(myData) * MAX_NUMBER)

will give you storage with zero overhead. You can then refer to the state of an instance at:

myDataBuffer[instanceId]

where instanceId is an integer.

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Thu Aug 16, 2018 6:42 pm
by fly135
Any time you allocate something on a heap it's going to add a few bytes for heap management. The heap management needs to know where the next block of free memory exists and the size of the current allocated block. The heap manager needs to be able to tell if it's management info is sane or corrupted. I don't know the details, but a few extra bytes per dynamically created object should be expected.

John A

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Fri Aug 17, 2018 12:43 am
by human890209
HI,
Thanks for your reply.

Thanks, fly135
I start to get the logic behind the memory management. I'm a newbie to C++. :D

Thanks, kolban
I use Class cause I got functions in it and I got Inheritance(I use the list to hold the base class's pointer). I don't know if I could replace class with struct.I never used a struct before... :mrgreen:
I want to create about 1.5k+ instances, I think maybe it's okay for the boards which aren't AVR Arduino.
After the simple test, I will test my target sketch and see the final result to decide if I should start to do the Struct Challenge. :lol:

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Fri Aug 17, 2018 7:45 pm
by fly135
human890209 wrote:I use Class cause I got functions in it and I got Inheritance(I use the list to hold the base class's pointer). I don't know if I could replace class with struct.I never used a struct before...
Kolban's approach only allocates one block of memory. So you only get the overhead of one heap object. I've used that approach, not for saving memory, but for knowing that I can deallocate one block and free it. Versus the potential of losing smaller blocks that are moving around queues and such.

In theory you should be able to do the same thing with classes. Allocate a block big enough to hold e.g. 1500 * sizeof(myClassType) and then break it up into 1500 pointers. Or dynamically calculating the pointer when needed based on index. You can have an init and deinit routine that works the same as a constructor/destructor.

John A

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Sat Aug 18, 2018 2:25 am
by human890209
In theory you should be able to do the same thing with classes. Allocate a block big enough to hold e.g. 1500 * sizeof(myClassType) and then break it up into 1500 pointers. Or dynamically calculating the pointer when needed based on index. You can have an init and deinit routine that works the same as a constructor/destructor.
Hi, fly125.
Your idea is to only malloc a big block once, which saves 1500 margins of the instances created with "new". I guess the big block size should be the biggest Class size * Capacity(1500).
Is every sell of the big buffer same size?
If the basic class A is 1byte in size, an inherent class B is 50 bytes in size(B: A), another inherent class C is 3 bytes in size (C : A). Is the block size should be 50 bytes*1500? Will that wastes some memory if the class B is not the majority instance?

Re: Why each instance of my class consume 20byte of memory at least?

Posted: Sat Aug 18, 2018 2:28 am
by human890209
Hi,
And I made more tests.
The results show that the margins of the new instance are not increasing, they only change in a pattern such as (6 6 12 6 6 12).
So if calculate the average of 100 instances' memory consumes, the number doesn't change.
So big memory won't cause lower efficiency.
My earlier assumption about the increase is wrong. ;)