JSF leaking memory through EL and composite compon

2019-03-27 16:27发布

问题:

Note: I'm using mojarra 2.1.20 and rich faces 4.2.2.

I've analyzed a heapdump and I've noticed that EL expressions reside in LRUMap in the session. Does anyone know why and what to do to avoid it?

The problem I have is related to a composite component containing following line:

  <rich:select ... valueChangeListener="#{cc.listValuesChangeListener}"

with backing bean my.package.MultiComboSelection. Obviously my.package.MultiComboSelection has a method defined named listValuesChangeListener.

The problem I see is that LRUMap contains ContextualCompositeMethodExpression (representation of the expression for valueChangeListener above), which cc attribute references MultiComboSelection. MultiComboSelection extends UINamingContainer and as such has parent/children properties - has references to component tree.

The result is that 16MB of memory cannot be garbage-collected because there is a reference chain:

session->LRUMap->ContextualCompositeMethodExpression->MultiComboSelection->parent and 16MB

The question is - why is it happening and how to fix it or work around it?

Class Name                                                                                   | Shallow Heap | Retained Heap | Retained Heap
--------------------------------------------------------------------------------------------------------------------------------------------
my.package.MultiComboSelection @ 0x78dc2bd50                                                 |           96 |    16 466 272 |    16 466 272
|- component javax.faces.component.UIComponentBase$FacetsMap @ 0x78dbbbd58                   |           48 |           128 |              
|- parent javax.faces.component.UIPanel @ 0x78dbbbdd8                                        |           88 |           760 |              
|- cc com.sun.faces.facelets.el.ContextualCompositeMethodExpression @ 0x78dc2bce0            |           32 |    16 466 384 |              
|  |- [0] java.lang.Object[2] @ 0x78dc2bc90                                                  |           24 |    16 466 464 |              
|  |  '- [0] java.lang.Object[1] @ 0x78dc2bc78                                               |           24 |    16 466 488 |              
|  |     '- [0] java.lang.Object[5] @ 0x78dc2bc20                                            |           40 |    16 466 576 |              
|  |        '- [0] java.lang.Object[2] @ 0x78dc2bc08                                         |           24 |    16 466 600 |              
|  |           '- [0] java.lang.Object[4] @ 0x78dc2bbe8                                      |           32 |    16 466 632 |              
|  |              '- value java.util.HashMap$Entry @ 0x78dc2bb40                             |           32 |    16 466 800 |              
|  |                 '- [1579] java.util.HashMap$Entry[2048] @ 0x78dbf61b8                   |        8 208 |    33 552 536 |              
|  |                    '- table java.util.HashMap @ 0x78dbb6860                             |           48 |    33 552 584 |              
|  |                       '- [1] java.lang.Object[2] @ 0x78ad95340                          |           24 |    33 552 608 |              
|  |                          '- value java.util.LinkedHashMap$Entry @ 0x78ad952c0           |           40 |    33 552 736 |              
|  |                             |- after, before java.util.LinkedHashMap$Entry @ 0x78acbe6a0|           40 |            40 |              
|  |                             |- [0] java.util.HashMap$Entry[2] @ 0x78ad952a8             |           24 |            24 |              
|  |                             |  '- table com.sun.faces.util.LRUMap @ 0x78ad95270         |           56 |    33 552 856 |              
--------------------------------------------------------------------------------------------------------------------------------------------

回答1:

The ContextualCompositeMethodExpression is referencing the whole composite component as instance variable as consequence of a fix to issue 1462. An user has reported about exactly this memory leak problem as issue 1940. And then later on the instance variable was marked transient as consequence of a fix to issue 1943. However, issue 1940 was for some reason been marked as duplicate of 1943. Two users have rightfully commented about exactly your problem in the bottom of issue 1940 that the memory leak issue is still open, but I'm not seeing any new issue reports related to that afterwards. The problem indeed only manifests when the composite component contains any method expression like a value change listener.

Theoretically, this problem can be workarounded by telling Mojarra to serialize the view state in session instead of keeping a reference to the view state. As the instance variable is marked transient, it would be bypassed. You can achieve that by the following context parameter in web.xml:

<context-param>
    <param-name>com.sun.faces.serializeServerState</param-name>
    <param-value>true</param-value>
</context-param>

Again, theoretically; I did not test that.

You might also want to try MyFaces instead, I can't tell that it would fix that, but I know that MyFaces 2.x is so far generally more careful than Mojarra as to state management, memory usage and performance.

In the meanwhile, I strongly recommend to create a new issue for Mojarra, referencing issue 1940, this Stack Overflow question and your findings. Referencing UI components in the view state is definitely not right. UI component instances are inherently request scoped, not view scoped.


Update: this was re-reported as issue 3198 which is fixed in Mojarra 2.2.8 and as per issue 3544 backported in Mojarra 2.1.29. So, if you upgrade to at least those versions, then this memory leak when you don't use com.sun.faces.serializeServerState=true (or javax.faces.SERIALIZE_SERVER_STATE=true as per JSF 2.2) should be fixed.



回答2:

We had a similar Issue with an Composite-Element with an actionListener. The Composite-Element collected a List of DataObjects, although they should be garbageCollected. We found out, list.clear() before reloading the List helps to prevent from this memory leak.



回答3:

This is probably fixed by https://java.net/jira/browse/JAVASERVERFACES-3544 on Mojarra.