Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

memory leak by using WiFiClient (40 Byte per usage) #1070

Closed
Links2004 opened this issue Nov 22, 2015 · 6 comments
Closed

memory leak by using WiFiClient (40 Byte per usage) #1070

Links2004 opened this issue Nov 22, 2015 · 6 comments

Comments

@Links2004
Copy link
Collaborator

test code:

void loop() {
    USE_SERIAL.printf("RAM: %d\n", ESP.getFreeHeap());
    // wait for WiFi connection
    if((WiFi.status() == WL_CONNECTED)) {
        WiFiClient * _tcp = new WiFiClient();
        _tcp->connect("192.168.1.12", 80);
        _tcp->stop();
        _tcp->~WiFiClient(); //does not help but shut abort any connection and free all
    }
    delay(100);
}

return:

RAM: 41312
RAM: 41312
RAM: 41048
RAM: 40816
RAM: 40584
RAM: 40352
RAM: 40104
RAM: 39872
RAM: 39640
RAM: 39408
RAM: 39176
RAM: 38944
RAM: 38712
RAM: 38480
RAM: 38248
RAM: 38016
RAM: 37784
@igrr
Copy link
Member

igrr commented Nov 22, 2015

#230 (comment)

@igrr
Copy link
Member

igrr commented Nov 22, 2015

You are doing active close. Send Connection: close header and wait for server to do active close.

@igrr igrr changed the title memory leek by using WiFiClient memory leak by using WiFiClient Nov 22, 2015
@Links2004
Copy link
Collaborator Author

can confirm this but the active close takes very long (60sec).
but i still miss 376Byte.

start: 41296
avter close timeout: 40920

void loop() {
    static int i = 10;
    static uint32_t last_ram = 0;
    uint32_t ram = ESP.getFreeHeap();
    USE_SERIAL.printf("RAM: %d (%d)\n", ram, (ram - last_ram));
    last_ram = ram;
    // wait for WiFi connection
    if((WiFiMulti.run() == WL_CONNECTED) && ram > 2000 && --i > 0) {

        WiFiClient * _tcp = new WiFiClient();
        _tcp->connect("192.168.1.12", 80);
        _tcp->stop();
        _tcp->~WiFiClient(); //does not help but shut abort any connection and free all
        delay(100);
    } else {
        delay(1000);
    }
}
[SETUP] WAIT 2...
[SETUP] WAIT 1...
RAM: 41296
RAM: 41296 (41296)
RAM: 41032 (-264)
RAM: 40800 (-232)
RAM: 40568 (-232)
RAM: 40336 (-232)
RAM: 40088 (-248)
RAM: 39856 (-232)
RAM: 39624 (-232)
RAM: 39392 (-232)
RAM: 39160 (-232)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 39160 (0)
RAM: 40696 (1536) <-- active close end?
RAM: 40920 (224)  <-- active close end?
RAM: 40920 (0)
RAM: 40920 (0)
RAM: 40920 (0)
RAM: 40920 (0)
RAM: 40920 (0)
RAM: 40920 (0)
RAM: 40920 (0)

@Links2004
Copy link
Collaborator Author

ok the server was not sending an ACK message to the FIN message, seams to be a anti flood protection if I add a _tcp->println("GET / HTTP/1.1"); I instand become the ACK for the close

but I still miss ~360 Byte (40 Byte per WiFiClient)
after several minutes they still not come back.

[SETUP] WAIT 2...
[SETUP] WAIT 1...
RAM: 41280
RAM: 41280 (41280)
RAM: 41208 (-72)
RAM: 41200 (-8)
RAM: 41128 (-72)
RAM: 41120 (-8)
RAM: 41048 (-72)
RAM: 41008 (-40)
RAM: 41000 (-8)
RAM: 40928 (-72)
RAM: 40920 (-8)
RAM: 40920 (0)
RAM: 40920 (0)
....

@Links2004 Links2004 changed the title memory leak by using WiFiClient memory leak by using WiFiClient (40 Byte per usage) Nov 22, 2015
@Links2004
Copy link
Collaborator Author

Note:

Never call the deconstructor directly! use delete!

good:

void loop() {
    USE_SERIAL.printf("RAM: %d\n", ESP.getFreeHeap());
    // wait for WiFi connection
    if((WiFi.status() == WL_CONNECTED)) {
        WiFiClient * _tcp = new WiFiClient();
        _tcp->connect("192.168.1.12", 80);
        _tcp->stop();
        delete _tcp;
    }
    delay(100);
}

pfabri added a commit to pfabri/Arduino that referenced this issue Dec 15, 2016
This branch adds a close_abort() method to the WiFiClient class.

How it works, what it does:
Calling `close_abort()` will close and abort the client connection it
is invoked on and, as a result, free its resources (i.e. memory).

**WARNING:** aborting connections without a good reason violates the
TCP protocol, because a closed connection would normally need to
spend some time in `TIME_WAIT` state before its resources are freed.

Usage example:
    WiFiClient client;		// set up a client

    { /* do things with your client */ }

    client.stop_abort()		// when you're done,abort the
				// connection if you must

Why it's useful:
1. Give programmers a way to shut down connections immediately if
   need be. The underlying `tcp.c` file has an abort function, but
   this has not been directly accessible via the `WiFiClient`
   class until now.

2. There are a number of reported issues for the repository
   addressing the heap corruption that can result from trying to
   retain too many connections in `TIME_WAIT` state (most notably:
   esp8266#230, esp8266#1070, esp8266#1923). Although the warning above holds, there may be
   circumstances where this isn't very important. For example an ESP8266
   running in AP mode hosting a page, which requests a new
   connection every second via an AJAX script to  monitor
   a sensor/button/etc. continusously.

Currently existing alternative approach:
When building a project, defining the
`-D MEMP_NUM_TCP_PCB_TIME_WAIT=5`compiler directive will limit the
maximum number of clients allowed to stay in TIME_WAIT state. `5` is
the default, lower it as necessary. See reference
[here](https://github.com/esp8266/Arduino/blob/master/tools/sdk/lwip/include/lwipopts.h#L263)

Thanks:
Thank you to @me-no-dev, @everslick and @Palatis for bringing the `
MEMP_NUM_TCP_PCB_TIME_WAIT` option to my attention.
@laercionit
Copy link
Contributor

I have this problem at the moment, using core 2.6.3, is this still in master or has it already been covered in this core?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants