Difference between revisions of "Digging memory leaks of EiffelStudio"

 
m (Identifying memory leak existence)
 
(13 intermediate revisions by 2 users not shown)
Line 5: Line 5:
 
=What is memory leak=
 
=What is memory leak=
 
==Definition==
 
==Definition==
<b>{{red| Memory leaks are objects that will never be used in the system and by any means are still be referenced directly or indirectly by once objects.}}</b>
+
<b>{{red| Memory leaks are objects that will never be used in the system and by any means are still be referred directly or indirectly by once objects.}}</b>
  
 
The objective of fixing a leak is find and cut one or more useless links to those once objects so that all the cycles/routes referenced can be garbage collected.
 
The objective of fixing a leak is find and cut one or more useless links to those once objects so that all the cycles/routes referenced can be garbage collected.
Line 11: Line 11:
 
One thing one should keep in mind is that memory leaks we are talking about are not due to the language or compiler defeats, but the programmer makes them by  
 
One thing one should keep in mind is that memory leaks we are talking about are not due to the language or compiler defeats, but the programmer makes them by  
 
programming illogically and incorrectly.
 
programming illogically and incorrectly.
==Examples==
+
 
 +
==Example==
 +
[[Image:Leak_example.PNG]]
 +
 
 +
As demonstrated in the diagram, simple object relations in the memory is presented. Now Window manager is referring two windows. If they are all valid windows, we say there is NO memory leak here. But had one, say window_1, been destroyed, the whole branch of window_1 were memory leak. The bad link from Window manager to Window_1 illogically remains and prevents garbage collecting.
  
 
= Fixing leaks by OC operation =
 
= Fixing leaks by OC operation =
==Identifying memory leaks existence==
+
==Identifying memory leak existence==
 
# Start debugging Eiffel Studio project, open a small project within the debugged Eiffel Studio.
 
# Start debugging Eiffel Studio project, open a small project within the debugged Eiffel Studio.
# Ctrl + Left click on Project setting toolbar button. You will see the GC Statistics window. Click "Men map", all objects in current system are listed with information of Object Type, Count and Delta. Object Type is just as the name implies. Count means the total number of that object type in current system. Delta means the variation of number between two "Men map" operations. Click Mem map again and again until you don't see Delta value any more. Note that clicking on the header of Delta sorts.
+
# Ctrl + Left click on Project setting toolbar button. You will see the GC Statistics window. Click "Mem map", all objects in current system are listed with information of Object Type, Count and Delta. Object Type is just as the name implies. Count means the total number of that object type in current system. Delta means the variation of number between two "Mem map" operations. Click Mem map again and again until you don't see Delta value any more. Note that clicking on the header of Delta sorts.
 
# Carefully switch to the debugger Eiffel Studio window and open a new window, Ctrl + N is recommended, in case any mouse moving causes unwanted object variation. Close the new window.
 
# Carefully switch to the debugger Eiffel Studio window and open a new window, Ctrl + N is recommended, in case any mouse moving causes unwanted object variation. Close the new window.
 
# Carefully switch back to the GC statistics window. Click on "Mem map" ONCE. Now you see under Delta the variations of all object. Ideally there should not be any delta value. If so you can hooray, otherwise, the real job of fixing memory leak just starts.
 
# Carefully switch back to the GC statistics window. Click on "Mem map" ONCE. Now you see under Delta the variations of all object. Ideally there should not be any delta value. If so you can hooray, otherwise, the real job of fixing memory leak just starts.
 +
In EiffelStudio, Ctrl + Alt + D brings up a hidden menu, the first item Memory Analyzer(MA) in which there is a Object Grid has almost the same function. Only note that before doing this one should disable auto refreshing. MA will be used for finding Back to Once routes.
  
 
==Identifying leaks in code ==
 
==Identifying leaks in code ==
  
  
===Choosing a starting object===
+
===Choosing a start object===
 
Normally a closed EB_DEVELOPMENT_WINDOW is still connected, which means that the object cycles (it could be any kind of routes) involving this window are somehow connected to one or more once objects. The reason we choose start from EB_DEVELOPMENT_WINDOW is that it is a core class representing an Eiffel Studio window and it relates almost all modules of interfaces such as menus, toolbars and various tools.  
 
Normally a closed EB_DEVELOPMENT_WINDOW is still connected, which means that the object cycles (it could be any kind of routes) involving this window are somehow connected to one or more once objects. The reason we choose start from EB_DEVELOPMENT_WINDOW is that it is a core class representing an Eiffel Studio window and it relates almost all modules of interfaces such as menus, toolbars and various tools.  
  
 
===Identifying the closed EB_DEVELOPMENT_WINDOW===
 
===Identifying the closed EB_DEVELOPMENT_WINDOW===
Expand the node of EB_DEVELOPMENT_WINDOW, you will see two nodes. You might guess there must be one that was already closed. Yes, we are going to find the closed one. EB_DEVELOPMENT_WINDOW is derived from EB_RECYCLABLE. This class is used to help us with the job. When the window is closed, {EB_DEVELOPMENT_WINDOW}.recycle is called.  Within the implementation we call `recycle' on almost all commands/tools so that references from those commands/tools are disconnected. By doing this, it is easy for us to find the closed window. Expand each of EB_DEVELOPMENT_WINDOW nodes, one with fewer client objects is the closed window. Same other leak objects like tools/commands can be found in the same way.
+
Expand the node of EB_DEVELOPMENT_WINDOW, you will see two nodes. You might guess there must be one that was already closed. Yes, we are going to find the closed one. EB_DEVELOPMENT_WINDOW is derived from EB_RECYCLABLE. This class is used to help us with the job. When the window is closed, {EB_DEVELOPMENT_WINDOW}.recycle is called.  Within the implementation we call `recycle' on almost all commands/tools so that references from those commands/tools are disconnected. By doing this, it is easy for us to find the closed window. Expand each of EB_DEVELOPMENT_WINDOW nodes, one with fewer client objects is the closed window. Some other leak objects like tools/commands can be found in the same way.
 +
 
 +
===Find out Back to Once Routes===
 +
Here we use the hidden tool in EiffelStudio activated by Ctrl + D, Memory Analyzer, to find out Back to Once routes which help pick leaks out. For the approach itself see [[Back_to_once_approach| Back to once approach]].
 +
 
 +
===Typical Back to Once Routes===
 +
There are several typical ways causing memory leak in EiffelStudio.
 +
 
 +
* <b>Preference related</b>
 +
Preferences are initialized and referred by once object (CELL). As we use many of {PREFERENCE}.change_actions to reflect a changes of any type of preference. This could  easily cause memory leaks. For example, when initializing a tool in a new opened window, we extend `agent {TOOL}.routine', into {PREFERENCE}.change_actions. This operation internally create a TUPLE [TOOL] referred by a PROCEDURE [TOOL, TUPLE] referred by a SPECIAL [PROCEDURE [ANY, TUPLE [ANY]]] of {PREFERENCE}.change_actions. When closing a window, if one do not remove this agent from {PREFERENCE}.change_actions, a leak occurs. Consequently, the object of a closed development window normally referred by the tool is not collected. Of course, other tools referred by this window will nor be collected.
 +
 
 +
Application related
 +
An object of EV_APPLICATION is referred by once object. The same as previous case, there are incorrectly extending *actions without removing will cause memory leaks.
 +
 
 +
* <b>Menuable or toolbarable commands</b>
 +
Once objects of menuable or toolbarable commands can easily cause memory leaks. See EB_MENUABLE_COMMAND and EB_TOOLBARABLE_COMMAND, when we do new_*_item, we get a new item for the interface. But one should notice that in creation routine of those interface classes, i.e. EB_COMMAND_TOOL_BAR_BUTTON, EB_SD_COMMAND_TOOL_BAR_BUTTON and EB_COMMAND_MENU_ITEM, the item itself is inserted into the command object. This means that if one do not call `recycle' on interface items when they are not used anymore, leaks occurs in the once command objects.
 +
 
 +
* <b>Once managers focusing on one window</b>
 +
Take EB_DEBUGGER_MANAGER as an example. `debugging_window: EB_DEVELOPMENT_WINDOW' refers to the window that debugging tools are raised or to be raised. When a window is closed, we need to check if this window is the `debugging_window' and refresh this field, otherwise, the closed window will be referred. And not only do memory leaks occur, but also would it cause crash when one trying to operate on the non-existing window.
 +
 
 +
* <b>EB_PREFERENCED_TOOL_BAR_TOGGLE_BUTTON not recycled</b>
 +
`synchronizer' need to `deregister_host' when the button is useless. Normally `deregister_host' removes agents from preferences. Not doing this would cause leaks of <b>Preference related</b>.
 +
 
 +
* <b>EB_EDITOR_TOKEN_GRID_SUPPORT not desynchronized</b>
 +
`desynchronize*' MUST be called at disposal when `synchronize*' was once called. Not doing this would cause leaks of <b>Preference related</b>.
 +
 
 +
* <b>TEXT_PANEL and this descendants not recycled</b>
 +
Not doing this, once instance of TEXT_PANEL_MANAGER would not remove the TEXT_PANEL from management.
 +
 
 +
===Difficult leaks===
 +
It possible that when you have done with the leaked window, you still see some leaks of small objects. For example, every time open and close a window we got 8 more TUPLE [INTEGER_32, INTEGER_32]. For the moment, it is very difficult to fix this kind of leaks -- increase of objects is a small part of a large number. The difficulty is that one can not easily find out which are leaks and which are not. To fix this kind of leaks, one need to know every piece of code that may have operation on the type of objects and carefully check the code to find out the reason.

Latest revision as of 14:29, 30 May 2007

Overview

The article is to help identify memory leaks of EiffelStudio and present one way of, to the maximum extend, getting rid of them. For now, with the tool in hand, it is difficult to guarantee that EiffelStudio is memory leak free. Every incorrect programmed operation could cause a leak, but not any operation we can check. One of the basic checks is opening a new window and close it, ensuring there is no leak by this operation. We call this OC operation. Though we can only do the basic to ensure what we are able to, this help one learn at programming in Eiffel how memory leak occurs and how to prevent from happening.

What is memory leak

Definition

Memory leaks are objects that will never be used in the system and by any means are still be referred directly or indirectly by once objects.

The objective of fixing a leak is find and cut one or more useless links to those once objects so that all the cycles/routes referenced can be garbage collected.

One thing one should keep in mind is that memory leaks we are talking about are not due to the language or compiler defeats, but the programmer makes them by programming illogically and incorrectly.

Example

Leak example.PNG

As demonstrated in the diagram, simple object relations in the memory is presented. Now Window manager is referring two windows. If they are all valid windows, we say there is NO memory leak here. But had one, say window_1, been destroyed, the whole branch of window_1 were memory leak. The bad link from Window manager to Window_1 illogically remains and prevents garbage collecting.

Fixing leaks by OC operation

Identifying memory leak existence

  1. Start debugging Eiffel Studio project, open a small project within the debugged Eiffel Studio.
  2. Ctrl + Left click on Project setting toolbar button. You will see the GC Statistics window. Click "Mem map", all objects in current system are listed with information of Object Type, Count and Delta. Object Type is just as the name implies. Count means the total number of that object type in current system. Delta means the variation of number between two "Mem map" operations. Click Mem map again and again until you don't see Delta value any more. Note that clicking on the header of Delta sorts.
  3. Carefully switch to the debugger Eiffel Studio window and open a new window, Ctrl + N is recommended, in case any mouse moving causes unwanted object variation. Close the new window.
  4. Carefully switch back to the GC statistics window. Click on "Mem map" ONCE. Now you see under Delta the variations of all object. Ideally there should not be any delta value. If so you can hooray, otherwise, the real job of fixing memory leak just starts.

In EiffelStudio, Ctrl + Alt + D brings up a hidden menu, the first item Memory Analyzer(MA) in which there is a Object Grid has almost the same function. Only note that before doing this one should disable auto refreshing. MA will be used for finding Back to Once routes.

Identifying leaks in code

Choosing a start object

Normally a closed EB_DEVELOPMENT_WINDOW is still connected, which means that the object cycles (it could be any kind of routes) involving this window are somehow connected to one or more once objects. The reason we choose start from EB_DEVELOPMENT_WINDOW is that it is a core class representing an Eiffel Studio window and it relates almost all modules of interfaces such as menus, toolbars and various tools.

Identifying the closed EB_DEVELOPMENT_WINDOW

Expand the node of EB_DEVELOPMENT_WINDOW, you will see two nodes. You might guess there must be one that was already closed. Yes, we are going to find the closed one. EB_DEVELOPMENT_WINDOW is derived from EB_RECYCLABLE. This class is used to help us with the job. When the window is closed, {EB_DEVELOPMENT_WINDOW}.recycle is called. Within the implementation we call `recycle' on almost all commands/tools so that references from those commands/tools are disconnected. By doing this, it is easy for us to find the closed window. Expand each of EB_DEVELOPMENT_WINDOW nodes, one with fewer client objects is the closed window. Some other leak objects like tools/commands can be found in the same way.

Find out Back to Once Routes

Here we use the hidden tool in EiffelStudio activated by Ctrl + D, Memory Analyzer, to find out Back to Once routes which help pick leaks out. For the approach itself see Back to once approach.

Typical Back to Once Routes

There are several typical ways causing memory leak in EiffelStudio.

  • Preference related

Preferences are initialized and referred by once object (CELL). As we use many of {PREFERENCE}.change_actions to reflect a changes of any type of preference. This could easily cause memory leaks. For example, when initializing a tool in a new opened window, we extend `agent {TOOL}.routine', into {PREFERENCE}.change_actions. This operation internally create a TUPLE [TOOL] referred by a PROCEDURE [TOOL, TUPLE] referred by a SPECIAL [PROCEDURE [ANY, TUPLE [ANY]]] of {PREFERENCE}.change_actions. When closing a window, if one do not remove this agent from {PREFERENCE}.change_actions, a leak occurs. Consequently, the object of a closed development window normally referred by the tool is not collected. Of course, other tools referred by this window will nor be collected.

Application related An object of EV_APPLICATION is referred by once object. The same as previous case, there are incorrectly extending *actions without removing will cause memory leaks.

  • Menuable or toolbarable commands

Once objects of menuable or toolbarable commands can easily cause memory leaks. See EB_MENUABLE_COMMAND and EB_TOOLBARABLE_COMMAND, when we do new_*_item, we get a new item for the interface. But one should notice that in creation routine of those interface classes, i.e. EB_COMMAND_TOOL_BAR_BUTTON, EB_SD_COMMAND_TOOL_BAR_BUTTON and EB_COMMAND_MENU_ITEM, the item itself is inserted into the command object. This means that if one do not call `recycle' on interface items when they are not used anymore, leaks occurs in the once command objects.

  • Once managers focusing on one window

Take EB_DEBUGGER_MANAGER as an example. `debugging_window: EB_DEVELOPMENT_WINDOW' refers to the window that debugging tools are raised or to be raised. When a window is closed, we need to check if this window is the `debugging_window' and refresh this field, otherwise, the closed window will be referred. And not only do memory leaks occur, but also would it cause crash when one trying to operate on the non-existing window.

  • EB_PREFERENCED_TOOL_BAR_TOGGLE_BUTTON not recycled

`synchronizer' need to `deregister_host' when the button is useless. Normally `deregister_host' removes agents from preferences. Not doing this would cause leaks of Preference related.

  • EB_EDITOR_TOKEN_GRID_SUPPORT not desynchronized

`desynchronize*' MUST be called at disposal when `synchronize*' was once called. Not doing this would cause leaks of Preference related.

  • TEXT_PANEL and this descendants not recycled

Not doing this, once instance of TEXT_PANEL_MANAGER would not remove the TEXT_PANEL from management.

Difficult leaks

It possible that when you have done with the leaked window, you still see some leaks of small objects. For example, every time open and close a window we got 8 more TUPLE [INTEGER_32, INTEGER_32]. For the moment, it is very difficult to fix this kind of leaks -- increase of objects is a small part of a large number. The difficulty is that one can not easily find out which are leaks and which are not. To fix this kind of leaks, one need to know every piece of code that may have operation on the type of objects and carefully check the code to find out the reason.