user40053: Problem
I have a few large objects in my code that need global visibility, but creating them seems to eat up memory significantly faster than simple math would dictate, and furthermore, using them differently in code seems to change how much RAM the compiler tells me I have left for local variables.
How do I optimize the use of my RAM while still being able to use an object in both my setup() and my loop() functions?
Note: Skim the Pseudo-Code section and then skip to the Discussion section if want to get to the meat of my question (I just wanted to provide plenty of background, if needed).
Test Code
To demonstrate that reported global RAM usage changes due not only the number of variables I define globally, but how those variables are used in code, I created a test program.  I compiled this code on the Arduino IDE on a Mac, and ran this on multiple Elegoo Nanos.
Please forgive the particularly long "minimal" complete verifiable code, but it has a bit going on.  Here's the pseudo-code for it.
Pseudo-Code
"T" (for tested) before a line means that I commented out a line or left it be depending on what test I was running:
Define a function to check how much memory remains, freeRam() (taken from https://playground.arduino.cc/Code/AvailableMemory, bottom of page)
Create a class that uses a ton of RAM, about 1024B (GinormousClass)
Create a wrapper for GinormousClass (GinormousClassWrapper) that may or may not construct the GinormousClass, based on how it is implemented

Global scope:
Run freeRAM() to see how much initial RAM is available before setup() is run.
T Create a global pointer to the wrapper class

Local scope (setup):
Report initial RAM
T Create a local pointer to the wrapper class
T Assign a newly constructed wrapper object to the pointer, which does NOT instantiate the huge class
    Report how much RAM remains after creation and after deletion
T Assign a newly constructed wrapper object to the pointer, which DOES instantiate the huge class
    Report how much RAM remains after creation and after deletion

Note: Remaining RAM before creation and after deletion were always the same.
Actual Code
#define ARRAY_SIZE 256

// Method to determine how much memory is left (https://playground.arduino.cc/Code/AvailableMemory, bottom of page)
int freeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
};

// Define a class that uses a ton of RAM
class GinormousClass {
  public:
    GinormousClass() { // Fill array with (kind of) random numbers
      for (int i = 0; i < ARRAY_SIZE; i++) {
        BIG[i] = random(1000);
      }
    };

    long BIG[ARRAY_SIZE]; // Have this class control a huge array
};

// Define a class that has the potential to use a ton of RAM
class GinormousClassWrapper {
  public:
    GinormousClassWrapper(bool createGiantObject) { // Constructor
      if (createGiantObject) {
        // Create a GinormousClass object (use a ton of RAM)
        gPointer = new GinormousClass();
      } else {
        // Do not create GinormousClass object
      }
    };

    ~GinormousClassWrapper() { // Delete the Ginormous Class, if it exists
      if (gPointer) {
        delete gPointer;
      }
    }

    GinormousClass *gPointer = NULL; // Initialize pointer to NULL
};

int initialFreeRam = freeRam(); // Figure out how much RAM is available before the fun begins
GinormousClassWrapper *ginny; // Declare a GLOBAL pointer to the potentially enormous class

void setup() {
  // Start the Serial connection, and get the initial amount of RAM
  Serial.begin(9600);
  Serial.print("Initial free RAM: ");
  Serial.println(initialFreeRam);

  //  GinormousClassWrapper *ginny; // Declare a LOCAL pointer to the potentially enormous class

  // Try making the class, without defining the array
  Serial.println("Making Ginormous Class (do nothing constructor)...");
  ginny = new GinormousClassWrapper(false);

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();

  // Delete the class
  Serial.println("Deleting first class...");
  delete ginny;

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();


  // Try making the class, WITH defining the array
  Serial.println("Making Ginormous Class (use a ton of RAM constructor)...");
  ginny = new GinormousClassWrapper(true);

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();

  //  Delete the class
  Serial.println("Deleting second class...");
  delete ginny;

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();
};

void loop() {

};

Results
I've collected the RAM reported at 4 different times:

after compilation
before setup()
after the "do-nothing" construction of the wrapper
after the "use-space" construction of the wrapper

5 different scenarios were tested:

no pointer declared/wrapper not constructed
pointer declared but object not constructed
do-nothing constructor used
use-space constructor used
do-nothing constructor used, object deleted, and use-space constructor used

Raw Results:
Numbers are amount of RAM left, in bytes
0 Not declared:
    Compiler: 1836
    Initial: 1830
    Do-nothing constructed: N/A
    Use-space constructed: N/A

1 Declared global, not constructed:
    Compiler: 1836
    Initial: 1830
    Do-nothing constructed: N/A
    Use-space constructed: N/A

2 Declared global, constructed do-nothing:
    Compiler: 1734
    Initial: 1728
    Do-nothing constructed: 1724
    Use-space constructed: N/A

3 Declared global, constructed use-space:
    Compiler: 1722
    Initial: 1716
    Do-nothing constructed: N/A
    Use-space constructed: 686

4 Declared global, constructed both:
    Compiler: 1648
    Initial: 1640
    Do-nothing constructed: 1636
    Use-space constructed: 610

Note: Local pointer instead of global pointer had no change except increase all reported RAM by 2 bytes.
Discussion
In the raw results, the GinormousClassWrapper takes 4B when constructed (the pointer it owns) and GinormousClass takes about 1024B when constructed (exactly 1030B, we can assume there's some overhead).  However, one strange thing is of note:
The amount of room stated by the compiler to be available for local variables decreases significantly when the wrapper is constructed  versus when it is not (1836B down to 1640B when both constructors are used).  I would expect that the amount of RAM used by globals would not change, because technically only the pointer is in global scope.  If it is SUPPOSED to change according to how the pointer is used, then why does it change by 196B, of all numbers?
I am assuming that the RAM given by the compiler is fact, and it's one that I would like to know because my project is running on tight memory restrictions.  So where is all of this valuable extra RAM going, and what can I do to reduce it?  Thank you for any help!

user-1: I have to confess that I probably doesn't understand what you are asking ...
Regarding the stats giving by the compiler (tested on a Uno, Arduino IDE 1.8.5), I got
Global Wrapper (using global ginny)
Sketch uses 3304 bytes (10%) of program storage space. Maximum is 32256 bytes.
Global variables use 400 bytes (19%) of dynamic memory, leaving 1648 bytes for local variables. Maximum is 2048 bytes.

Execution
Initial free RAM: 1640
Making Ginormous Class (do nothing constructor)...
Current free RAM: 1636

Deleting first class...
Current free RAM: 1640

Making Ginormous Class (use a ton of RAM constructor)...
Current free RAM: 610

Deleting second class...
Current free RAM: 1640

Local Wrapper (using local ginny)
Sketch uses 3258 bytes (10%) of program storage space. Maximum is 32256 bytes.
Global variables use 398 bytes (19%) of dynamic memory, leaving 1650 bytes for local variables. Maximum is 2048 bytes.

Execution
Initial free RAM: 1642
Making Ginormous Class (do nothing constructor)...
Current free RAM: 1638

Deleting first class...
Current free RAM: 1642

Making Ginormous Class (use a ton of RAM constructor)...
Current free RAM: 612

Deleting second class...
Current free RAM: 1642

Local Wrapper version uses 2 bytes less of global space. That's correct, because space for local ginny comes from the stack, while global ginny comes from the heap.
No compiler will give you how much space (for variables) will you need during execution. That's unsolvable (from Turing's Halting Problem). Just think what happens when you enter in a non-ending recursion.
Comparing execution results, both version show the same amount of memory, off by two bytes, which is due to ginny declared global vs local.
There is a differente between free space declared by compiler (1648, 1650 bytes) and space reported during program execution (1640, 1642 bytes). The difference is 8 bytes in both cases.
Now, setup is a function called from main. That's means you need stack space for the call (return address and whatever), which explains the 8 bytes difference.

40053: <h1>Problem</h1>

<p>I have a few large objects in my code that need global visibility, but creating them seems to eat up memory significantly faster than simple math would dictate, and furthermore, using them differently in code seems to change how much RAM the compiler tells me I have left for local variables.</p>

<p><strong>How do I optimize the use of my RAM while still being able to use an object in both my <code>setup()</code> and my <code>loop()</code> functions?</strong></p>

<p>Note: Skim the <strong>Pseudo-Code</strong> section and then skip to the <strong>Discussion</strong> section if want to get to the meat of my question (I just wanted to provide plenty of background, if needed).</p>

<h1>Test Code</h1>

<p>To demonstrate that reported global RAM usage changes due not only the number of variables I define globally, but <strong>how those variables are used in code</strong>, I created a test program.  I compiled this code on the Arduino IDE on a Mac, and ran this on multiple Elegoo Nanos.</p>

<p>Please forgive the particularly long "minimal" complete verifiable code, but it has a bit going on.  Here's the pseudo-code for it.</p>

<h3>Pseudo-Code</h3>

<p>"T" (for tested) before a line means that I commented out a line or left it be depending on what test I was running:</p>

<pre><code>Define a function to check how much memory remains, freeRam() (taken from https://playground.arduino.cc/Code/AvailableMemory, bottom of page)
Create a class that uses a ton of RAM, about 1024B (GinormousClass)
Create a wrapper for GinormousClass (GinormousClassWrapper) that may or may not construct the GinormousClass, based on how it is implemented

Global scope:
Run freeRAM() to see how much initial RAM is available before setup() is run.
T Create a global pointer to the wrapper class

Local scope (setup):
Report initial RAM
T Create a local pointer to the wrapper class
T Assign a newly constructed wrapper object to the pointer, which does NOT instantiate the huge class
    Report how much RAM remains after creation and after deletion
T Assign a newly constructed wrapper object to the pointer, which DOES instantiate the huge class
    Report how much RAM remains after creation and after deletion
</code></pre>

<p>Note: Remaining RAM before creation and after deletion were always the same.</p>

<h3>Actual Code</h3>

<pre><code>#define ARRAY_SIZE 256

// Method to determine how much memory is left (https://playground.arduino.cc/Code/AvailableMemory, bottom of page)
int freeRam() {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &amp;v - (__brkval == 0 ? (int) &amp;__heap_start : (int) __brkval);
};

// Define a class that uses a ton of RAM
class GinormousClass {
  public:
    GinormousClass() { // Fill array with (kind of) random numbers
      for (int i = 0; i &lt; ARRAY_SIZE; i++) {
        BIG[i] = random(1000);
      }
    };

    long BIG[ARRAY_SIZE]; // Have this class control a huge array
};

// Define a class that has the potential to use a ton of RAM
class GinormousClassWrapper {
  public:
    GinormousClassWrapper(bool createGiantObject) { // Constructor
      if (createGiantObject) {
        // Create a GinormousClass object (use a ton of RAM)
        gPointer = new GinormousClass();
      } else {
        // Do not create GinormousClass object
      }
    };

    ~GinormousClassWrapper() { // Delete the Ginormous Class, if it exists
      if (gPointer) {
        delete gPointer;
      }
    }

    GinormousClass *gPointer = NULL; // Initialize pointer to NULL
};

int initialFreeRam = freeRam(); // Figure out how much RAM is available before the fun begins
GinormousClassWrapper *ginny; // Declare a GLOBAL pointer to the potentially enormous class

void setup() {
  // Start the Serial connection, and get the initial amount of RAM
  Serial.begin(9600);
  Serial.print("Initial free RAM: ");
  Serial.println(initialFreeRam);

  //  GinormousClassWrapper *ginny; // Declare a LOCAL pointer to the potentially enormous class

  // Try making the class, without defining the array
  Serial.println("Making Ginormous Class (do nothing constructor)...");
  ginny = new GinormousClassWrapper(false);

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();

  // Delete the class
  Serial.println("Deleting first class...");
  delete ginny;

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();


  // Try making the class, WITH defining the array
  Serial.println("Making Ginormous Class (use a ton of RAM constructor)...");
  ginny = new GinormousClassWrapper(true);

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();

  //  Delete the class
  Serial.println("Deleting second class...");
  delete ginny;

  Serial.print("Current free RAM: ");
  Serial.println(freeRam());
  Serial.println();
};

void loop() {

};
</code></pre>

<h1>Results</h1>

<p>I've collected the RAM reported at 4 different times:</p>

<ol>
<li>after compilation</li>
<li>before <code>setup()</code></li>
<li>after the "do-nothing" construction of the wrapper</li>
<li>after the "use-space" construction of the wrapper</li>
</ol>

<p>5 different scenarios were tested:</p>

<ol>
<li>no pointer declared/wrapper not constructed</li>
<li>pointer declared but object not constructed</li>
<li>do-nothing constructor used</li>
<li>use-space constructor used</li>
<li>do-nothing constructor used, object deleted, and use-space constructor used</li>
</ol>

<h3>Raw Results:</h3>

<p>Numbers are amount of RAM left, in bytes</p>

<pre><code>0 Not declared:
    Compiler: 1836
    Initial: 1830
    Do-nothing constructed: N/A
    Use-space constructed: N/A

1 Declared global, not constructed:
    Compiler: 1836
    Initial: 1830
    Do-nothing constructed: N/A
    Use-space constructed: N/A

2 Declared global, constructed do-nothing:
    Compiler: 1734
    Initial: 1728
    Do-nothing constructed: 1724
    Use-space constructed: N/A

3 Declared global, constructed use-space:
    Compiler: 1722
    Initial: 1716
    Do-nothing constructed: N/A
    Use-space constructed: 686

4 Declared global, constructed both:
    Compiler: 1648
    Initial: 1640
    Do-nothing constructed: 1636
    Use-space constructed: 610
</code></pre>

<p>Note: Local pointer instead of global pointer had no change except increase all reported RAM by 2 bytes.</p>

<h1>Discussion</h1>

<p>In the raw results, the GinormousClassWrapper takes 4B when constructed (the pointer it owns) and GinormousClass takes about 1024B when constructed (exactly 1030B, we can assume there's some overhead).  However, one strange thing is of note:</p>

<p>The amount of room stated by the compiler to be available for local variables <strong>decreases significantly</strong> when the wrapper is constructed  versus when it is not (1836B down to 1640B when both constructors are used).  I would <strong>expect</strong> that the amount of RAM used by globals would not change, because technically only the pointer is in global scope.  If it is SUPPOSED to change according to how the pointer is used, then why does it change by 196B, of all numbers?</p>

<p>I am assuming that the RAM given by the compiler is fact, and it's one that I would like to know because my project is running on tight memory restrictions.  <strong>So where is all of this valuable extra RAM going, and what can I do to reduce it?</strong>  Thank you for any help!</p>
 -1: <p><em>I have to confess that I probably doesn't understand what you are asking ...</em></p>

<p>Regarding the stats giving by the compiler (tested on a Uno, Arduino IDE 1.8.5), I got</p>

<p><strong>Global Wrapper (using global ginny)</strong></p>

<pre><code>Sketch uses 3304 bytes (10%) of program storage space. Maximum is 32256 bytes.
Global variables use 400 bytes (19%) of dynamic memory, leaving 1648 bytes for local variables. Maximum is 2048 bytes.
</code></pre>

<p><strong>Execution</strong></p>

<pre><code>Initial free RAM: 1640
Making Ginormous Class (do nothing constructor)...
Current free RAM: 1636

Deleting first class...
Current free RAM: 1640

Making Ginormous Class (use a ton of RAM constructor)...
Current free RAM: 610

Deleting second class...
Current free RAM: 1640
</code></pre>

<p><strong>Local Wrapper (using local ginny)</strong></p>

<pre><code>Sketch uses 3258 bytes (10%) of program storage space. Maximum is 32256 bytes.
Global variables use 398 bytes (19%) of dynamic memory, leaving 1650 bytes for local variables. Maximum is 2048 bytes.
</code></pre>

<p><strong>Execution</strong></p>

<pre><code>Initial free RAM: 1642
Making Ginormous Class (do nothing constructor)...
Current free RAM: 1638

Deleting first class...
Current free RAM: 1642

Making Ginormous Class (use a ton of RAM constructor)...
Current free RAM: 612

Deleting second class...
Current free RAM: 1642
</code></pre>

<p>Local Wrapper version uses 2 bytes less of global space. That's correct, because space for <code>local ginny</code> comes from the stack, while <code>global ginny</code> comes from the heap.</p>

<p>No compiler will give you how much space (for variables) will you need during execution. That's unsolvable (from Turing's Halting Problem). Just think what happens when you enter in a non-ending recursion.</p>

<p>Comparing execution results, both version show the same amount of memory, off by two bytes, which is due to <code>ginny</code> declared global vs local.</p>

<p>There is a differente between free space declared by compiler (1648, 1650 bytes) and space reported during program execution (1640, 1642 bytes). The difference is 8 bytes in both cases.</p>

<p>Now, <code>setup</code> is a function called from <code>main</code>. That's means you need stack space for the call (return address and whatever), which explains the 8 bytes difference.</p>
