Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 358658

Summary: Roughly 40% of the heap being eaten by EclipseContexts
Product: [Eclipse Project] Platform Reporter: Remy Suen <remy.suen>
Component: UIAssignee: Platform UI Triaged <platform-ui-triaged>
Status: CLOSED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: bsd, daniel_megert, david_williams, dj.houghton, pwebster
Version: 4.2   
Target Milestone: ---   
Hardware: PC   
OS: Mac OS X   
Whiteboard: stalebug
Attachments:
Description Flags
Screenshot depicting the state in question. none

Description Remy Suen CLA 2011-09-22 16:09:54 EDT
Created attachment 203862 [details]
Screenshot depicting the state in question.

The vast majority of the space being taken up for the top four instances seems to be eaten by the context's map of listeners.
Comment 1 Remy Suen CLA 2011-09-22 16:10:45 EDT
Comment on attachment 203862 [details]
Screenshot depicting the state in question.

That was dumb. Why use a screenshot when I could've just copy/pasted the text?

-------------

241 instances of "org.eclipse.e4.core.internal.contexts.EclipseContext", loaded by "org.eclipse.e4.core.contexts" occupy 200,295,344 (38.84%) bytes. 

Biggest instances:

org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d5bd4140 - 33,131,720 (6.42%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d3ad1248 - 32,860,720 (6.37%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d5bd40b0 - 29,201,976 (5.66%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d3602520 - 26,254,768 (5.09%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7da62d760 - 6,631,232 (1.29%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7e35121b0 - 5,287,240 (1.03%) bytes. 
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7e3510d40 - 5,256,160 (1.02%) bytes.
Comment 2 Remy Suen CLA 2011-09-23 08:06:59 EDT
Looking at my own inner, I have over 300 listeners for a part, a workbench window, and for the application.

DJ also seems to have over 300 listeners but his contexts, as shown in comment 1, is eating about 30 megs of memory each.
Comment 3 Oleg Besedin CLA 2011-09-23 11:32:51 EDT
I can't reproduce this on either Windows or Mac.

That said, I do get somewhat different picture for Mac. For my typical workspace I get ~3Mb used by contexts on Windows vs. ~20Mb used on Mac.
Comment 4 Oleg Besedin CLA 2011-09-23 15:38:29 EDT
I tightened up a bit memory allocation by providing default sizes for new maps
and removing empty entries. 

http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?id=ca5404eb7ab1e116df0736da131764508e97e533

I don't think this will have any effect on the actual problem reported here,
but just in case...

It might be that the central problem here was leaking menu elements from the
History view which were tested for visibility and were registered with
contexts.
Comment 5 Remy Suen CLA 2011-09-26 16:08:40 EDT
My memory usage was going up so I took a heap dump. 118.2 MB of 298.4 MB was being eaten by the context.
Comment 6 Brian de Alwis CLA 2011-09-28 13:50:52 EDT
I've been hitting this problem too.  MAT reports 727,487 instances of EclipseContext$ComputationReference.

"Dominant Tree". showing the root objects with the highest retained heap size shows roughly ~92MB of ~380 MB:


Class Name                                                        | Shallow Heap | Retained Heap | Percentage
--------------------------------------------------------------------------------------------------------------
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d42caa38|           88 |    14,957,928 |      4.24%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d42caa68|           88 |    14,504,984 |      4.11%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d4a3f370|           88 |    13,587,736 |      3.85%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d4a3f2e0|           88 |    13,421,384 |      3.80%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d72bdc08|           88 |     9,913,624 |      2.81%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d72bdc98|           88 |     9,397,112 |      2.66%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d72bdd28|           88 |     8,882,152 |      2.52%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7d6e440f8|           88 |     5,342,048 |      1.51%
org.eclipse.e4.core.internal.contexts.EclipseContext @ 0x7dcd47010|           88 |     2,846,184 |      0.81%
--------------------------------------------------------------------------------------------------------------


Histogram:


Class Name                                                               |   Objects | Shallow Heap | Retained Heap
--------------------------------------------------------------------------------------------------------------------
org.eclipse.e4.core.internal.contexts.EclipseContext$ComputationReference|   727,487 |   17,459,688 | >= 52,379,064
Total: 21 of 13,476 entries                                              | 5,872,947 |  352,864,000 |              
--------------------------------------------------------------------------------------------------------------------
Comment 7 Oleg Besedin CLA 2011-09-29 15:39:44 EDT
I made another change to reduce memory allocations/deallocations:

http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?id=16334e911487b3ea6c2d0a941fcd79faad8363ab

I still can't reproduce the issue. For me, the heap memory gets used quite a bit and I see memory usage go up, but it does get released on GC.
Comment 8 Oleg Besedin CLA 2011-09-30 10:43:04 EDT
The memory that gets used up in the "idle" state - profiler shows it being consumed by two sources: 
 (1) HandledContributionItem$ToolItemUpdateTimer.run() ~75% of allocated memory
 (2) DefaultSpellingEngine.check() ~20% of the allocated memory 

Strangely, profiler shows that large portion of the memory allocated in (1) comes from this simple method in BindingTableManager:
	private String getTableId(String id) {
		return BINDING_TABLE_PREFIX + id;
	}

This method allocates ~2Mb every few seconds:
java.lang.StringBuilder.toString() ~0.8 Mb ~10K objects ~77 bytes/object
java.lang.StringBuilder.<init>(String) ~0.4 Mb ~5K objects ~80 bytes/object
java.lang.StringBuilder.append(String)	~0.7 Mb ~5K objects ~137 bytes/object
Comment 9 Oleg Besedin CLA 2011-10-04 16:33:31 EDT
I made another change, removing ComputationReferences and replacing them with a WeakHashMap. This should make the space to be easier to reclaim for GC.

http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?id=b83abc04227c706cc4dc03b242f7f65befab9224
Comment 10 Oleg Besedin CLA 2011-10-05 15:19:17 EDT
I changed how we record and process dependencies to reduce the number of listeners.

Say, we have context1 <- context2 <- context3 <- context4. 

The context 1 has a value for the key "a" and a context function description under name of "func". Say, func() = context.get("a") + 1;

If we ask about the func() on the context 4 we'll get the following dependencies:

context1
 ValueComputation depends on "a", "func"
context2
 ValueComputation depends on "a", "func", "parent"
context3
 ValueComputation depends on "a", "func", "parent"
context4
 ValueComputation depends on "a", "func", "parent"

all in all: 11 dependencies for a computation that access one variable.

I changed the code to track dependencies only on the actual context that originated the query. Using the example above:

context1
 - no dependencies
context2
 - no dependencies
context3
 - no dependencies
context4
 ValueComputation depends on "a", "func"

In the new scheme, when a context value changed we propagate notification of changes to child context.

CPU-wise, the "old" way is slightly more efficient when we process large number of updates on computations that do not change. The "new" way is more efficient when dealing with computations that are added/removed. For SDK use case, the "new" way should be slightly more efficient CPU-wise.

Memory-wise the "new" way should be an improvement as we drastically reduce the number of recorded dependencies.

And it makes code simpler :-).

http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?id=c8fc7f3886a4eac187d304f106637bf24a9331ee
Comment 11 Brian de Alwis CLA 2011-10-12 19:04:39 EDT
(In reply to comment #10)
> I changed how we record and process dependencies to reduce the number of
> listeners.

Oleg's change seems to have made a big difference — I've run the entire day without having to restart.  Yay!
Comment 12 Eclipse Genie CLA 2020-01-24 08:39:37 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 13 Brian de Alwis CLA 2020-01-27 13:37:40 EST
Marking as CLOSED-FIXED.