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 184 bytes at a time #1923

Closed
yomasa opened this issue Apr 17, 2016 · 54 comments
Closed

memory leak 184 bytes at a time #1923

yomasa opened this issue Apr 17, 2016 · 54 comments

Comments

@yomasa
Copy link

yomasa commented Apr 17, 2016

Basic Infos

looks like 184 bytes get lost every time you post to the page ie :'http://192.168.0.6/gpio/1'

Hardware

Hardware: ESP-12, wemos mini, esp-7
Core Version: 2.2.0-rc1

memory leak?

using the example sketch WifiWebServer added one line of code at the end of sketch

Serial.println( ESP.getFreeHeap(),DEC);

Settings in IDE

Module: wemos mini
Flash Size: ?4MB/1MB?
CPU Frequency: ?80Mhz?
Flash Mode: ?qio?
Flash Frequency: ?40Mhz?
Upload Using: ?OTA / SERIAL?
Reset Method: ?ck / nodemcu?

Sketch

/*

  • This sketch demonstrates how to set up a simple HTTP-like server.
  • The server will set a GPIO pin depending on the request
  • http://server_ip/gpio/0 will set the GPIO2 low,
  • http://server_ip/gpio/1 will set the GPIO2 high
  • server_ip is the IP address of the ESP8266 module, will be
  • printed to Serial when the module is connected.
    */

include <ESP8266WiFi.h>

const char* ssid = "your-ssid";
const char* password = "your-password";

// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);

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

// prepare GPIO2
pinMode(2, OUTPUT);
digitalWrite(2, 0);

// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");

// Start the server
server.begin();
Serial.println("Server started");

// Print the IP address
Serial.println(WiFi.localIP());
}

void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}

// Wait until the client sends some data
Serial.println("new client");
while(!client.available()){
delay(1);
}

// Read the first line of the request
String req = client.readStringUntil('\r');
Serial.println(req);
client.flush();

// Match the request
int val;
if (req.indexOf("/gpio/0") != -1)
val = 0;
else if (req.indexOf("/gpio/1") != -1)
val = 1;
else {
Serial.println("invalid request");
client.stop();
return;
}

// Set GPIO2 according to the request
digitalWrite(2, val);

client.flush();

// Prepare the response
String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n\r\n\r\nGPIO is now ";
s += (val)?"high":"low";
s += "\n";

// Send the response to the client
client.print(s);
delay(1);
Serial.println("Client disonnected");

// The client will actually be disconnected
// when the function returns and 'client' object is detroyed

Serial.println( ESP.getFreeHeap(),DEC);

}

@igrr
Copy link
Member

igrr commented Apr 17, 2016

This memory should be reclaimed after a couple of minutes, once TCP connection exists TIME_WAIT state. Are you sure that free heap doesn't return to the original value after a few minutes?

@gpepe
Copy link

gpepe commented Apr 18, 2016

not leek, but 20k eating this code:

`
#include <Arduino.h>
#include <ESP8266WiFi.h>

WiFiServer server(80);

const char* ssid = "???";
const char* password = "???";

static const char eatHeap[] =
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" \
"\r\n";

size_t orig;

void setup()
{
WiFi.begin(ssid, password);
server.begin();
orig = ESP.getFreeHeap();
}

void loop()
{
WiFiClient client = server.available();
if (client)
{
String s;
s += eatHeap;
s += orig;
s += "/";
s += ESP.getFreeHeap();
s += "";
client.print(s);
client.flush();
client.stop();
}
}

`

@gpepe
Copy link

gpepe commented Apr 18, 2016

and f5 key down esp die :))

@gpepe
Copy link

gpepe commented Apr 18, 2016

edit tcp.c and recompile lwip to stop heap eating:

/* Check if this PCB has stayed long enough in TIME-WAIT */
if ((u32_t)(tcp_ticks - pcb->tmr) > 10) {
// if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
++pcb_remove;
}

F5 never die :))

@igrr
Copy link
Member

igrr commented Apr 18, 2016

ESP8266WebServer library handles this stuff for you, without breaking TCP protocol spec.

@igrr igrr closed this as completed Apr 26, 2016
@rafichris
Copy link

so this isn't a memory leak issue it is just performing according the tcp spec? In my case I use ajax to do some interaction between user and the server every second.
Is there an elegant way to prevent memory is running out?

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.
@hmiez
Copy link

hmiez commented Apr 4, 2017

I am a little bit confused about the wifi client heap issue.

I checked: 184 heap bytes are really lost each time we receive a request (this formerly produced a hangup in my application), BUT leaks are sometimes retreived after a while (a while = several minutes).
See below the result of a modified WiFiWebServer, with timings. We can see that after 3 mins (187s) we have the same heap that after 11s.

If the number of client requests is somewhat high, any app will quickly run in troubles.

Even when the client is destroyed in the app (local client variable after the function has returned) or if the client is stopped (client.stop()) the TCP connection should be closed.
But, in my tests, no memory is returned !!

Does anyone has an idea how to proceed to leave a clean situation without waiting for minutes ?

(my configuration: Arduino IDE 1.8.1 / esp8266 SDK 2.3.0 as mentionned in the arduino menu)

Connecting to xxx...... connected
Server started @ 192.168.1.13
time : 6s received: GET /gpio/0 HTTP/1.1 heap is now: 45056
time : 6s received: GET /favicon.ico HTTP/1.1 heap is now: 44872
time : 10s received: GET /gpio/0 HTTP/1.1 heap is now: 44688
time : 10s received: GET /favicon.ico HTTP/1.1 heap is now: 44504
time : 11s received: GET /gpio/0 HTTP/1.1 heap is now: 44320
time : 12s received: GET /favicon.ico HTTP/1.1 heap is now: 44136
time : 12s received: GET /gpio/0 HTTP/1.1 heap is now: 43952
time : 12s received: GET /favicon.ico HTTP/1.1 heap is now: 43768
time : 44s received: GET /gpio/0 HTTP/1.1 heap is now: 43584
time : 44s received: GET /favicon.ico HTTP/1.1 heap is now: 43400
time : 81s received: GET /gpio/0 HTTP/1.1 heap is now: 43216
time : 81s received: GET /favicon.ico HTTP/1.1 heap is now: 43032
time : 111s received: GET /gpio/0 HTTP/1.1 heap is now: 42848
time : 111s received: GET /favicon.ico HTTP/1.1 heap is now: 42664
time : 187s received: GET /gpio/0 HTTP/1.1 heap is now: 44320
time : 187s received: GET /favicon.ico HTTP/1.1 heap is now: 44136
time : 223s received: GET /gpio/0 HTTP/1.1 heap is now: 44320
time : 223s received: GET /favicon.ico HTTP/1.1 heap is now: 44136

@psy0rz
Copy link

psy0rz commented May 1, 2017

since that pull request isnt merged yet and it will take some time to reach upstream, here's a workaround that actually cleanups the TIME_WAITS and frees memory:

// do this before including other stuff like wificlient.h
#include "lwip/tcp_impl.h"

void tcpCleanup()
{
  while(tcp_tw_pcbs!=NULL)
  {
    tcp_abort(tcp_tw_pcbs);
  }
}

update: This goes in your main file. Call tcpCleanup regulary, for example from your loop() function.

@donfrench
Copy link

psy0rz: Thank you very much! I have been struggling with this problem for way too long. This did the job!!! However, it did take me a little while to figure out the details and just for others who are as dumb as me, here is what I didn't get at first. The code you posted goes into the user's app. At first I thought it went into tcp.c like one of the previous suggestions. Also, what was pretty obvious but maybe should be said is that you need a call to tcpCleanup somewhere in the loop( ). I put it at the bottom. Again, thanks for solving this problem for me! You're a peach!

@pieman64
Copy link

@igrr there are quite a few threads referring to memory leaks and I'm pretty sure many of Espressif's clients will be suffering with it in some way or other.

My first question relates to the free Heap change from 2.3.0 to the latest master branch. One of our sketches has 35kb free with 2.3.0 and only 30kb free with the master branch. Why is this and is there any easy way of recovering the 5kb?

Below is a sketch that tries to implement the memory leak fix indicated in this thread. It seems to do the job but there are still some unknown occasions when the leak appears to be permanent. Do you have any idea how ESP users can ensure the free Heap always remains unchanged?

/* HeapCheckV3.ino by Costas 12 June 2017 was originally WebUdater.ino Example
   Line 15 of ESP8266HTTPUpdateServer.cpp in 
   C:\arduino-1.8.3portable\arduino-1.8.3\hardware\esp8266com\esp8266\libraries\ESP8266HTTPUpdateServer\src
   changed to:      </body></html>)";  // was </body></html>\n)";
   To upload through terminal you can use: curl -F "[email protected]" esp8266-webupdate.local/update
*/
#define HEAPCHECKER 0        // set to 1 to test HEAP loss fix
#ifdef HEAPCHECKER
  #include "lwip/tcp_impl.h" // losing bytes work around
#endif

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>

const char* host = "esp8266-webupdate";
const char* ssid = "xxxxxxxxxx";
const char* password = "xxxxxxxxxx";
uint32_t originalram;

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup(void){

  Serial.begin(115200);
  Serial.println();
  Serial.println("Booting Sketch...");
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);

  while(WiFi.waitForConnectResult() != WL_CONNECTED){
    WiFi.begin(ssid, password);
    Serial.println("WiFi failed, retrying.");
  }

  MDNS.begin(host);

  httpUpdater.setup(&httpServer);
  httpServer.begin();

  MDNS.addService("http", "tcp", 80);
  Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
  originalram = ESP.getFreeHeap();
}

//
void tcpCleanup()   // losing bytes work around
{
  while(tcp_tw_pcbs!=NULL)
  {
    tcp_abort(tcp_tw_pcbs);
  }
}
//

void loop(void){
  httpServer.handleClient();
  delay(1000);
  uint32_t ram = ESP.getFreeHeap();
  Serial.printf("RAM: %d  change %d\n", ram, (ram - originalram ));
  if(HEAPCHECKER){          // losing bytes work around
    tcpCleanup();           
    Serial.printf("tcpCleanup completed\n");
  }
}

@igrr
Copy link
Member

igrr commented Jun 13, 2017

The RAM usage difference between 2.3.0 and master is tracked in this ticket: #3227.

Regarding permanent heap loss, AFAIK there was a MEMP_NUM_TCP_PCB_TIME_WAIT option implemented in #2260, which by default limits the number of PCBs in TIME_WAIT state to 5.
If you have a test case against master branch which shows that some memory is not reclaimed after a while, please share it in a new issue. Thanks.

@victorconka
Copy link

@psy0rz Thanx man ! that did the trick !!!!!!!!! Telegram is a piece of memory hog there, and my esp would reboot every time heap memory goes under 6500~6000(¿bits/bytes?). With that piece of code thing gets stable:
heap memory usage:
(no message sent) 8752; 8752; 8752; 8752;
(1st message) 8088;
(2nd and n'th messages) 7904 ; 7904; 7904; 7904; 7904; 7904; 7904; 7904

@mribble
Copy link
Contributor

mribble commented Oct 8, 2017

Thanks!!! I was actually actually hitting an exception:

Exception 29: StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores

I was using ajax and was consuming 34KB of memory in about 1 minute.

Looked at the stack and saw it was probably a failed malloc. Saw this was a 184 byte issue and google lead me to this thread. Now it's worked around in 5 minutes and I was prepared for a long debug session. I'm generally a low level programmer, but the pieces are so easy to put together with this esp8266 that I'm making huge progress. I really appreciate this community!

Hope to see this mainlined so others don't hit it.

@d-a-v
Copy link
Collaborator

d-a-v commented Oct 9, 2017

Does it look like #2925 ?

@mribble
Copy link
Contributor

mribble commented Oct 9, 2017

d-a-v, it looks similar to #2925 and I think it is likely related. However, there are some differences in the original bug description there and what I'm seeing. The original bug there says it doesn't happen for them with version 2.3.0, but I am seeing the problem on that version. Also they say the problem only happens with messages over 2048 bytes, and my messages are well under that size.

I am fine sharing my code by giving access to a private git repo (my code will eventually be open sourced, but I'm keeping it private until the kickstarter project launches). However, it does require some special hardware to run. Perhaps a better solution is I'm happy to test a solution when one becomes available.

@d-a-v
Copy link
Collaborator

d-a-v commented Oct 9, 2017

So you can give a try to #3362 (you'll use lwip-v2 instead of v1.4).
Please report back if this helps.

@mribble
Copy link
Contributor

mribble commented Oct 9, 2017

I tried master and that actually solved my issue. It consumed 4 KB more memory than the hack above, but it didn't crash like 2.3.0.

Then I tried #3362 and it seemed to cause my esp8266 to reboot without any kind of exception posted to serial (normally I see the exception and can decode the dump file). Not sure what was happening. I think any esp8266 should reproduce this issue. I posted my code and some libraries you'll need to reproduce the issue at: http://glacialwanderer.com/files/2017/Esp8266.zip

@d-a-v
Copy link
Collaborator

d-a-v commented Oct 9, 2017

Glad to know it works now.
about #3362, I did try with your sources. I observed the same behaviour as you did: crash with it (no crash without, webserver returning plain text 'null'). A simple sketch reproducing this behaviour is needed to sort this out (or diving into your code with the appropriate attached hardware). I did not succeed seeing the gdb magic string @ either 115200 nor 74880bps.
Your sketch is an example why #3362 should leave the choice to use lwip1.4 for sorting things out.

@victorconka
Copy link

@mribble, thanx for sharing your experience. It made me try the master branch which seems to be more compact or compiler friendly or whatever... Now I have enough free heap for safe execution of my code.
ESP.restart and ESP.reboot still don't work properly, but no unexpected reboot thanks to code @psy0rz provided .

@mribble
Copy link
Contributor

mribble commented Mar 24, 2018

@Tizan,

It looks like the FAQ helped get you from not compiling to the code compiling. That is a step in the right direction. Now the problem is you still have a memory leak. Where are you calling tcpCleanup()? It should be after every client.stop().

You also asked how you reuse a connection, which is the better way to fix this. This is handled by the place making these httprequests (perhaps an ajax webpage?). There if you just keep reusing the httprequest then new versions won't be created by this esp8266 code and things will run much faster than it would if you just work around this memory 'leak'.

@Tizana
Copy link

Tizana commented Mar 24, 2018

thx @mribble for your answer,

so the tcp_abort(tcp_tw_pcbs); function did not triggered when i tried open connection/send data with my android phone but it work with Hercules SETUP utility software installed on my PC.

image

1- first i connect to the ESP
2- send the data
3-last send the CARRIAGE RETURN ASCII code.

the ESP receive the data and transmit to the serial, close the connection and then start the cleaning
here's the serial monitor

image

but when i try with my android app that i created with xamarin the esp receive the data and transmit it to the serial and close connection but never clean the TCP. the more i send data the more i got memory leak, here's what i got in serial :

image

this is so weird why the ESP accept connection from both HERCULES and Android App extract the data and send it back to the serial and then disconnect. but if the connection was made through HERCULES app the ESP start cleaning but not with my android app ?
can anybody know why ?

is there any debug option that i can look for to see what happen exactly ? i only have this one defined in my code #define DEBUG_ESP_MDNS_ERR

@mribble
Copy link
Contributor

mribble commented Mar 24, 2018

I'm not sure how your android or pc apps are implemented or if they have the correct options to avoid a new connection for each data transfer.

If you were willing to write your own webpage with javascript (this wouldn't be very hard as I learned how to do it about 6 months ago in a few hours) then you would control this by calling:

new XMLHttpRequest();

only once. If you called it once for each transfer then you'll have many connections and have this memory problem where esp8266 needs to track all the connections because the tcp spec says it should.

As for tools that let you see the raw tcp packet headers, I haven't ever had to do that so I'm not sure what exists. There are probably software tools that let you see the headers for tcp packets. Certainly hardware like this would let you see all this data.

@donfrench
Copy link

donfrench commented Mar 25, 2018 via email

@Tizana
Copy link

Tizana commented Mar 25, 2018

i maybe know why tcp_abort was not executed when i used my android app (developed with Xamarin) as client.
if i connect the android app to the esp and send data, the tcp_abort won't be executed until i close the app. putting the app in the background won't work the app should be killed(closed).

for example in my android app i have a button send which is responsible to connect to the ESP and send a data.
if i open the APP and click three time to button send the tcp_abort will not be executed at all, once i close(kill) the app the tcp_abort is executed three time in a row.

for information i add the tcpCleanup(); two time, in the top of the loop and in the bottom.

void loop() 
{
tcpCleanup();`
...........
..........
tcpCleanup();`
}

i don't have a knowledge in the TCP Protocol so i can't explain why this happen and how the TCP deal with these situation.

i succeeded to get around with this problem in my Xamarin app but i'm not convinced with my solution.

@mribble
Copy link
Contributor

mribble commented Mar 29, 2018

In my javascript I created a ringbuffer of XMLHttpRequest objects so that I could reuse these. My understanding is this should reuse the connection and prevent the 184 byte memory growth. This is important to me because I'm using a dynamic ajax that causes 4 transfers per second and I'll quickly exhaust memory.

This worked fine for lwIP v1.4 Higher Bandwidth. It also works for lwIP v2.0 Higher Bandwidth. However the default is now lwIP v2.0 Lower Memory, and it causes the memory to grow until I run out of memory. I find this odd that you are growing the table of connections even when I'm reusing connections. It seems like there might be a bug here, but I haven't looked at this code. It feels like perhaps you are only reusing the latest connection (since I have a small ring buffer that doesn't help me), but still the code is wasting memory to track more than just one connection. Could someone explain if this makes sense?

Calling tcpCleanup() does work around the issue.

@d-a-v
Copy link
Collaborator

d-a-v commented Mar 29, 2018

without hacking sources, with tcpdump you can check if the same connection is indeed reused just by looking at the TCP port number.
The difference between the two versions of lwip2 is only MSS so from the App point of view, especially if data are small there shouldn't be any difference.

@icssww
Copy link

icssww commented Mar 30, 2018

regarding this code below when i include "lwip/tcp_impl.h" i got an error , no such file or directory, so include it the whole directory "C:/ ........lwip/tcp_impl.h"

I have been searching a lot, still unable to figure out how to get this library? Where to place this library? Will appreciate any support, thanks

@d-a-v
Copy link
Collaborator

d-a-v commented Mar 30, 2018 via email

@icssww
Copy link

icssww commented Mar 30, 2018

thanks for the prompt response @d-a-v , can you please help me with the link to FAQ/documentation, apologies for the inconvinience

@d-a-v
Copy link
Collaborator

d-a-v commented Mar 30, 2018

It's right on your screen:
https://github.com/esp8266/Arduino/
then Content > Documentation > latest > FAQ

@icssww
Copy link

icssww commented Mar 30, 2018

Thanks for your prompt response, it helped big time

@MrSuhov
Copy link

MrSuhov commented Apr 2, 2018

Seems like I am facing this issue in universal-telegram-bot where I have to call bot.Update method every second resulting in Exception (29) when heap falls to about 6k.
Workaround with cleaning TCP PCBs doesn't work for me (https://arduino-esp8266.readthedocs.io/en/latest/faq/readme.html).
Board: wemos d1, compile options below:
compile01
Running out of heap in about 15 minutes.
Measuring heap size before and after bot.Update() method ends with this:
Free heap before bot.Update: 7032, free heap after bot.Update: 6648
Free heap before bot.Update: 6976, free heap after bot.Update: 6896
Free heap before bot.Update: 6920, free heap after bot.Update: 6536
Free heap before bot.Update: 6864, free heap after bot.Update: 6784
Free heap before bot.Update: 6808, free heap after bot.Update: 6616
Free heap before bot.Update: 6752, free heap after bot.Update: 6368
Free heap before bot.Update: 6696, free heap after bot.Update: 6616
Free heap before bot.Update: 6640, free heap after bot.Update: 6256
Free heap before bot.Update: 6584, free heap after bot.Update: 6200
Free heap before bot.Update: 6528, free heap after bot.Update: 6144
Free heap before bot.Update: 6472, free heap after bot.Update: 6280
Free heap before bot.Update: 6416, free heap after bot.Update: 6032
Free heap before bot.Update: 6360, free heap after bot.Update: 6280
Free heap before bot.Update: 6304, free heap after bot.Update: 5920
Free heap before bot.Update: 6248, free heap after bot.Update: 6168
Free heap before bot.Update: 6192, free heap after bot.Update: 5808
Free heap before bot.Update: 6136
Exception (29):
epc1=0x4020e88a epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000004 depc=0x00000000

Secure client is used for establishing connection to telegram bot api. Any ideas on how to overcome this?

@d-a-v
Copy link
Collaborator

d-a-v commented Apr 4, 2018

@MrSuhov if you are using 2.4.1 core, there is a known leak which is fixed in the current git version.
tcpCleanup still applies though.

@MrSuhov
Copy link

MrSuhov commented Apr 4, 2018

@d-a-v Thanks a lot! A fresh git clone https://github.com/esp8266/Arduino.git esp8266 was really helpful - heap keeps on a constant level!

@felixbarzin
Copy link

@d-a-v many thanks :)

@d-a-v
Copy link
Collaborator

d-a-v commented May 29, 2018

FYI, tcpCleanup is not anymore needed in latest git.

@ivelichkevich
Copy link

Is it possible to get the spot fix?

@victorconka
Copy link

@ivelichkevich personally i've moved on from this usecase towards using raspberry pi zero/orange pi zero as a server which does all the logic, including telegram commands/messages/etc.
esp8266 acts as a simple node that communicates via mqtt with the server. Has plenty of free memory now. Hope that's useful

@d-a-v
Copy link
Collaborator

d-a-v commented Dec 2, 2018

Is it possible to get the spot fix?

What does it mean ?

personally i've moved on from this usecase towards using (linux)

That's a choice. There's less fun :)

This issue is not around anymore since core-2.4.2.
For previous core version, it is well described in the FAQ.

@ivelichkevich
Copy link

ivelichkevich commented Dec 2, 2018

What does it mean ?

commits hashes for this fix or pease of code to fix leak in 2.4.1
I can use 2.4.2

Sorry, Where can I find FAQ?

@d-a-v
Copy link
Collaborator

d-a-v commented Dec 2, 2018

The FAQ is in the documentation. Documentation link has been recently made more visible in the main page (Quick Links) in https://github.com/esp8266/Arduino/.

How it has been fixed is an idea of @me-no-dev:

Links are there:
links & macro and lwip-v2 patch

This patch will be proposed to lwIP developpers at some point.

@ivelichkevich
Copy link

The FAQ is in the documentation. Documentation link has been recently made more visible in the ...
...
This patch will be proposed to lwIP developpers at some point.

Thanks!

@akamming
Copy link

akamming commented Oct 9, 2021

Hi,

i also have this "memory leak" issue on an ESP which is quite busy (constantly updating LED's). the pattern is that sometimes, but not always the heap is decreased by 184 bytes after the call to ESP8266 Webserver handleclient. And some random time (sometimes after a few minutes, sometimes after several hours) a lot of calls to handleclient cause this 184 byte heap decrease, resulting in no more heap memory and a restart of the ESP (due to watchdog or exception)

I applied the "solution in the FaQ" with the tcpcleanup() function. Result is that no more memory is leaked at first, but then again after some random time (again somewhere between a few minutes or a few hours) the handleclient starts "leaking" a lot of memory again... restarting the ESP.

The application is a wordclock, so the restart is very annoying... I know this is an old topic, but hopefully anyone can give me a hint how to fix...

my code can be found here

@RobertGnz
Copy link
Contributor

RobertGnz commented Nov 25, 2022

Hi akamming and fellows, I tried tcpcleanup() and it doesn't work because
" while (tcp_tw_pcbs) "
never trigger the call to tcp_abort(tcp_tw_pcbs)

Why ? The answer is because the instruction "extern struct tcp_pcb* tcp_tw_pcbs;" does NOT initialise tcp_tw_pcbs to the one used by the Client. It is only a declaration and because it is only a declaration, tcp_tw_pcbs is set to NULL. Consecuently tcp_abort(tcp_tw_pcbs) is never called.

On the following lines of my message I indicate an efficient way to sove the problem. For this I added an API to WiFiClient class. Here is the code:

In WiFiClient.h add the code and comments:

// Api for saving precious heap.
// When Client class is used by a Server: Client = Server.available(), sockets in TIME_WAIT remain after
// issuing Client.stop by the Server application.
// This reduce drastically the heap memory in case of multiple client connections to the server ending
// with a Server shutdown and an ESP8266 reboot after some hours because of insufficient heap memory.
// This API is provided to free heap memory by mean of closing sockets just before issuing a Client.stop
// by the Server.
// The Server must use this API just before calling Client.stop
// Note: none of the proposed methode e.g. tcpCleanup and tcp_kill_timewait works properly.
void abortTimeWait();

In WiFiClient.cpp add the code and comments:

// Api for heap saving. Must be call just before WiFiClient::stop().
void WiFiClient::abortTimeWait() {
if (!_client) return;
tcp_pcb* p;
p = _client->getPCB();
if (p) tcp_abort(p);
}

With this code you will not be annoyed by sockets in TIME_WAIT state after stopping clients.

I would be gratefull if someone could make a pull request in order to put this API in the GIT repository for use by everybody because I am not familiar with pull requests.

Robert
(sorry for my poor English, I am French)

@akamming
Copy link

I Fixed my problem by using another led library. The one i used had nointerrupts() builtin, which apparently caused my issue

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