JSF пропускает память через EL и составные компоненты

Примечание: я использую mojarra 2.1.20 и богатые лица 4.2.2.

Я проанализировал heapdump, и я заметил, что EL-выражения находятся в LRUMap в сеансе. Кто-нибудь знает, почему и что делать, чтобы избежать этого?

Проблема, которую я имею, связана с составным компонентом, содержащим следующую строку:

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

с поддержкой bean my.package.MultiComboSelection. Очевидно, my.package.MultiComboSelection имеет метод, определенный именем listValuesChangeListener.

Я вижу, что LRUMap содержит ContextualCompositeMethodExpression (представление выражения для valueChangeListener выше), который атрибут cc ссылается на MultiComboSelection. MultiComboSelection расширяет UINamingContainer и, как таковой, имеет свойства parent/children - имеет ссылки на дерево компонентов.

Результат состоит в том, что 16 МБ памяти не могут быть собраны в мусор, поскольку существует цепочка ссылок:

session- > LRUMap- > ContextualCompositeMethodExpression- > MultiComboSelection- > Родитель и 16 МБ

Вопрос: почему это происходит, как его исправить или обойти?
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

ContextualCompositeMethodExpression ссылается на весь составной компонент как переменную экземпляра как следствие исправления на issue 1462. Пользователь сообщил об этой проблеме утечки памяти как issue 1940. А затем позже переменная экземпляра была отмечена transient как следствие исправления для issue 1943. Тем не менее, выпуск 1940 года по какой-то причине был отмечен как дубликат 1943 года. Два пользователя по праву прокомментировали вашу проблему в нижней части проблемы 1940, что проблема утечки памяти по-прежнему открыта, но я не вижу никаких новых сообщений о проблемах к этому впоследствии. Проблема действительно проявляется только тогда, когда составной компонент содержит любое выражение метода, такое как слушатель изменения значения.

Теоретически эту проблему можно обойти, говоря Моджарре о сериализации состояния представления в сеансе, вместо того, чтобы ссылаться на состояние представления. Поскольку переменная экземпляра отмечена transient, она будет обходить. Этого можно достичь с помощью следующего контекстного параметра в web.xml:

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

Опять теоретически; Я не тестировал это.

Возможно, вы также захотите попробовать MyFaces, я не могу сказать, что это исправит это, но я знаю, что MyFaces 2.x до сих пор в целом более осторожен, чем Mojarra, в отношении управления состоянием, использования памяти и производительности.

В то же время я настоятельно рекомендую создать новую тему для Mojarra, ссылаясь на проблему 1940, этот вопрос и ваши выводы. Ссылка на компоненты пользовательского интерфейса в состоянии просмотра определенно неверна. Экземпляры компонентов пользовательского интерфейса по своей сути запрашивают область видимости, а не область просмотра.


Обновить: это было повторно указано как issue 3198, которое исправлено в Mojarra 2.2.8 и в соответствии с вопрос 3544, представленный в Mojarra 2.1.29. Таким образом, если вы обновляете, по крайней мере, те версии, то эта утечка памяти, когда вы не используете com.sun.faces.serializeServerState=true (или javax.faces.SERIALIZE_SERVER_STATE=true в соответствии с JSF 2.2), должна быть исправлена.

Ответ 2

У нас была аналогичная проблема с композитным элементом с actionListener. Композитный элемент собрал a List of DataObjects, хотя они должны быть garbageCollected. Мы обнаружили, что list.clear() перед перезагрузкой списка помогает предотвратить утечку этой памяти.