ESPAsyncWebServer icon indicating copy to clipboard operation
ESPAsyncWebServer copied to clipboard

Memory Leak When Ending/Resetting

Open av4625 opened this issue 3 years ago • 3 comments

Versions

Board: ESP32 Arduino IDE: 1.8.19 Arduino ESP32 core version: 2.0.4 ESPAsyncWebServer: 1.2.3

Issue

When I start the server for the first time it takes a good size chunk of RAM. This is fine and expected. When I end the server it doesn't give back all the memory that it used. Every start and end after this takes roughly the same amount of memory and also gives it back. So it seems that the first start takes memory and holds it and it doesn't get freed.

Code Showing Issue

#include <cstdint>
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
#include <ezButton.h>
#include <WiFi.h>

const uint8_t page_length{70};
const uint8_t page[] PROGMEM = {
    31, 139, 8, 8, 22, 115, 43, 99, 0, 3, 105, 110, 100, 101, 120,
    95, 109, 105, 110, 46, 104, 116, 109, 108, 0, 179, 81, 76, 201, 79,
    46, 169, 44, 72, 205, 40, 201, 205, 177, 179, 41, 176, 243, 72, 205,
    201, 201, 215, 81, 8, 207, 47, 202, 73, 81, 180, 209, 47, 176, 227,
    2, 0, 8, 9, 148, 205, 35, 0, 0, 0
};

AsyncWebServer server{80};
ezButton button{25};

void setup()
{
    Serial.begin(115200);
    button.setDebounceTime(50);
    Serial.print("Initial: ");
    Serial.println(ESP.getFreeHeap());

    // Pinning to core 0, as I have issues when starting WiFi on core 1
    xTaskCreatePinnedToCore(
        task,
        "Main Controller",
        10000,
        NULL,
        5,
        NULL,
        0); 
}

void loop()
{
    vTaskDelete(NULL);
}

void task(void*)
{
    while (true)
    {
        button.loop();

        if (button.isPressed())
        {
            start_server();
            Serial.print("Server started: ");
            Serial.println(ESP.getFreeHeap());
        }
        else if (button.isReleased())
        {
             end_server();
             Serial.print("Server ended: ");
             Serial.println(ESP.getFreeHeap());
        }

        // Don't block the idle task
        delay(1);
    }
}

void start_server()
{
    static const char* ssid{"fancy_name"};
    static const char* password{"secretpass"};

    // Access point
    WiFi.mode(WIFI_AP);
    // Max 1 connection
    WiFi.softAP(ssid, password, 1, 0, 1);

    MDNS.begin(ssid);

    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/a", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/b", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/c", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/d", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/e", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/f", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/g", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/h", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/i", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.on("/j", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        AsyncWebServerResponse *response = request->beginResponse_P(
            200, "text/html", page, page_length);
        response->addHeader("Content-Encoding", "gzip");
        request->send(response);
    });

    server.onNotFound([](AsyncWebServerRequest *request) {request->send(404);});

    server.begin();
}

void end_server()
{
    server.end();
    server.reset();
    MDNS.end();
    WiFi.softAPdisconnect(true);
    WiFi.mode(WIFI_OFF);
}

I use a button to start and stop the server and use ezButton just to make it easier. The button can be removed and it can be started and stopped every 1-2 seconds for example.

Output

You can see after the server ends, it never gets above 265100.

Initial: 297260
Server started: 231960
Server ended: 265100
fancy_name
Server started: 231784
Server ended: 265100

Code "Without" Issue

#include <cstdint>
#include <ESPAsyncWebServer.h>
#include <ESPmDNS.h>
#include <ezButton.h>
#include <WiFi.h>

const uint8_t page_length{70};
const uint8_t page[] PROGMEM = {
    31, 139, 8, 8, 22, 115, 43, 99, 0, 3, 105, 110, 100, 101, 120,
    95, 109, 105, 110, 46, 104, 116, 109, 108, 0, 179, 81, 76, 201, 79,
    46, 169, 44, 72, 205, 40, 201, 205, 177, 179, 41, 176, 243, 72, 205,
    201, 201, 215, 81, 8, 207, 47, 202, 73, 81, 180, 209, 47, 176, 227,
    2, 0, 8, 9, 148, 205, 35, 0, 0, 0
};

AsyncWebServer server{80};
ezButton button{25};

void setup()
{
    Serial.begin(115200);
    button.setDebounceTime(50);
    Serial.print("Initial: ");
    Serial.println(ESP.getFreeHeap());

    // Pinning to core 0, as I have issues when starting WiFi on core 1
    xTaskCreatePinnedToCore(
        task,
        "Main Controller",
        10000,
        NULL,
        5,
        NULL,
        0); 
}

void loop()
{
    vTaskDelete(NULL);
}

void task(void*)
{
    while (true)
    {
        button.loop();

        if (button.isPressed())
        {
            start_server();
            Serial.print("Server started: ");
            Serial.println(ESP.getFreeHeap());
        }
        else if (button.isReleased())
        {
             end_server();
             Serial.print("Server ended: ");
             Serial.println(ESP.getFreeHeap());
        }

        // Don't block the idle task
        delay(1);
    }
}

void start_server()
{
    static const char* ssid{"fancy_name"};
    static const char* password{"secretpass"};

    // Access point
    WiFi.mode(WIFI_AP);
    // Max 1 connection
    WiFi.softAP(ssid, password, 1, 0, 1);

    MDNS.begin(ssid);
}

void end_server()
{
    MDNS.end();
    WiFi.softAPdisconnect(true);
    WiFi.mode(WIFI_OFF);
}

Output

You can see that the initial is the same, and when the end_server function is called the free RAM goes back up much closer than it did when using ESPAsyncWebServer. Although it does show that there is possibly a similar but smaller issue with WiFi or MDNS.

Initial: 297620
Server started: 250872
Server ended: 282456
fancy_name
Server started: 250704
Server ended: 282448

It is very possible that I am not ending the server properly, but I can't find anything in the documentation about it.

av4625 avatar Sep 21 '22 22:09 av4625

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 18 '23 14:06 stale[bot]

I saw something similar. Every time I create/delete a new server instance, some memory is leaked. I suspect on the handlers not being always properly freed. This is what I tried:

  1. create: new server
  2. set handlers: server.on(...)
  3. start the server: server.begin()
  4. after 2 or 3 minutes, and without doing any request to the server: delete server
  5. after 1 minute, get back to step 1

On each cycle you lose some memory... unless you omit step 2

kmomberg avatar Feb 12 '24 22:02 kmomberg

[STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future.

stale[bot] avatar Feb 12 '24 22:02 stale[bot]