Page MenuHomePhabricator

Memory Leak efl_add
Closed, InvalidPublic1 Story Points

Description

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include<Eina.h>
#include<Efl.h>
#include <Elementary.h>

EAPI_MAIN int
elm_main(int argc, char **argv)
{
   Evas_Object *win,*rect;

   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);

   win = elm_win_util_standard_add("Main", "App");
   elm_win_autodel_set(win, EINA_TRUE);
  
  while(1){
   rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS,win);
   efl_del(rect);
  }
   
   evas_object_show(win);
   elm_run();

   return 0;
}
ELM_MAIN()

gcc -c source.c pkg-config --cflags --libs elementary

following part


while(1){
 rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS,win);
 efl_del(rect);
}

Will keep increasing memory untill application crash

ali.alzyod updated the task description. (Show Details)Apr 8 2019, 9:45 AM

@raster Can you please take look at this?

raster closed this task as Invalid.Apr 14 2019, 4:01 AM

because evas object deletions are DEFERRED. they require render cycles to drive the deletion. they will be kept around for 2 render cycles so evas can do a prev state vs current state check., imagine u delete a visible object - how can evas know what changes - what area is occupied that now needs re-rendering? was it visible or not, was it set to a color of 0 0 0 0 which EFFECTIVELY made it invisible to rendering even if it was shown... thus objects invisibly have extra references to them that are removed if delete flags are set by the render cycles, so the above case is just "invalid" in that any real process will never do the above and will have a fairly limited upper bound on how many objects will be created then deleted before a render cycle happens. if there really is a very big spate of creating invisible objects (e.g. you're using evas as an image processing engine where you might load an image file into an image object then save it out as another format) then there is evas_norender(evas); which does a render cycle without doing the actual rendering thus driving the object deletion pump. it's an incredibly rare day this is ever needed and if it is used, any updates from objects that really did need a render will be lost. you would need to manually add a damaged region for the whole canvas output to force a full re-render to avoid this if this ever were the case.

@raster Thank you for clear it out.

My case, is that :
I made fix for TextBlock memory leak issue, and I wanted to test it by creating many objects and remove them, So I think I will force render each specific time duration.

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include<Eina.h>
#include<Efl.h>
#include <Elementary.h>

static void _btn_clicked(void * data,Eo * obj, void * eventInfo){
   int i = 300000;
   Eo * win = (Eo*) data;
   while(i--){
      Eo * rect = efl_add(EFL_CANVAS_RECTANGLE_CLASS,win);
      efl_del(rect);
   }
}

EAPI_MAIN int
elm_main(int argc, char **argv)
{
   Evas_Object *win;

   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);

   win = elm_win_util_standard_add("Main", "App");
   elm_win_autodel_set(win, EINA_TRUE);
   

   Eo * bt = elm_button_add(win);
   evas_object_smart_callback_add(bt,"clicked",_btn_clicked,win);
   elm_object_text_set(bt,"add & delete");
   evas_object_resize(bt,100,50);
   evas_object_show(bt);

   evas_object_resize(win,320,480);
   
   evas_object_show(win);
   elm_run();

   return 0;
}
ELM_MAIN()

With this code I notice something,
1- That application will locate memory first time you hit "add & delete" button. (this memory will not be free in future).
2- When you hit "add & delete" again, memory will not be increased(it looks like reused object or there memory).

because the canvas didn't go through a render cycle - nothing changed visually thus it didn't render. it has to actually go through a full cycle. try show some of those rects and leave them there for 1 frame.... then delete them.

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include<Eina.h>
#include<Efl.h>
#include <Elementary.h>

#define RECT_COUNT 100000
Eo* rect_objs[RECT_COUNT] = {0};
int bCreated = 0;

Eo * bt,*win;

static void _btn_clicked(void * data,Eo * obj, void * eventInfo){
   Eo * win = (Eo*) data;
   evas_object_hide(bt);
   if(!bCreated){
      for(int i = 0 ; i < RECT_COUNT ; i++){
         rect_objs[i] = efl_add(EFL_CANVAS_RECTANGLE_CLASS,win);
         evas_object_resize(rect_objs[i],10,10);
         evas_object_move(rect_objs[i],100,50);
      }
      bCreated = 1;
   } else {
      for(int i = 0 ; i < RECT_COUNT ; i++){
         efl_del(rect_objs[i]);
         rect_objs[i] = NULL;
      }
      bCreated = 0;
   }
   evas_object_show(bt);
}

EAPI_MAIN int
elm_main(int argc, char **argv)
{
   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);

   win = elm_win_util_standard_add("Main", "App");
   elm_win_autodel_set(win, EINA_TRUE);
   
   bt = elm_button_add(win);
   evas_object_smart_callback_add(bt,"clicked",_btn_clicked,win);
   elm_object_text_set(bt,"add & delete");
   evas_object_resize(bt,100,50);
   evas_object_show(bt);

   evas_object_resize(win,320,480);
   
   evas_object_show(win);
   elm_run();

   return 0;
}
ELM_MAIN()

@raster Even If I add objects, and see them on screen then delete them, memory is still not freed. (In the above example first time you click on the button, it add objects, second time you click it will delete them)

I think this is some internal object reusing mechanism, isn't there some code in efl_object that takes pointers from the trasher ? I guess simular things are in Evas, this will add up to something like the mem increasing that you see

raster added a comment.EditedApr 16 2019, 11:09 AM

you'd have to stress it and re-use a lot of objects to see if there is a leak. we do have memory pools and trash etc. like @bu5hm4n says - we alloc and keep around that memory then re-use it again. you'd have to do a lot of allocs over and over and then see the mem footprint keep going up and up and never stabilizing. i adapted your code a bit to do this with a timer ever 0.01 sec:

#define EFL_EO_API_SUPPORT 1
#define EFL_BETA_API_SUPPORT 1
#include<Eina.h>
#include<Efl.h>
#include <Elementary.h>

Eo *o1 = NULL, *o2 = NULL;

static Eina_Bool cb(void *data)
{
   Eo *win = data;

   if (o2)
     {
        efl_del(o2);
     }
   o2 = o1;

   o1 = efl_add(EFL_CANVAS_RECTANGLE_CLASS, win);
   evas_object_move(o1, rand()&0xff, rand()&0xff);
   evas_object_resize(o1, 10 + rand()&0xff, 10 + rand()&0xff);
   evas_object_color_set(o1, rand()&0xff, rand()&0xff, rand()&0xff, 255);
   evas_object_show(o1);
   return EINA_TRUE;
}

EAPI_MAIN int
elm_main(int argc, char **argv)
{
   Evas_Object *win;

   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);

   win = elm_win_util_standard_add("Main", "App");
   elm_win_autodel_set(win, EINA_TRUE);

   evas_object_resize(win, 500, 500);

   ecore_timer_add(0.01, cb, win);

   evas_object_show(win);
   elm_run();

   return 0;
}
ELM_MAIN()

and using massif the results are exactly as predicted - they do go up and then stabilize once pools are full and get re-used. this is by design to keep memory around for re-use later. see massif visualizer output:

http://www.enlightenment.org/ss/e-5cb619f7dbb3a8.63821831.png

I think it is make sense to reuse memory, instead of realloc

Are there any configuration to set limit for reused memory?
Or can I force system to free it?

there isn't a single control. evas has various caches, edje has, there are mempools and those are set up by code, and so on etc. etc. - so you'd have to tweak various things to force free and some cant be force freed at all without modifying the code to no longer use a pool etc.

Thanks for all the information