Аспекты сканирования слишком большого количества классов и кэш методов заполняет память - PullRequest
6 голосов
/ 04 ноября 2010

В нашем приложении у нас есть несколько (всего около 30) веб-сервисов.Каждый веб-сервис находится в своем собственном файле WAR и имеет собственный контекст Spring, который инициализируется при запуске приложения.

У нас также есть ряд классов аспектов, управляемых аннотациями, которые мы применяем к классам веб-сервисов.Вначале выражение poincut выглядело так:

@Pointcut("execution(public * my.package.service.business.*BusinessServiceImpl.*(..))")
  public void methodsToBeLogged() {
  }

И AOP был включен для сервисов посредством входа в конфигурацию.

Но когда число веб-сервисов выросло, мы начали испытывать OutOfMemoryException с на наших серверах.После некоторого профилирования и анализа оказалось, что память занята кешем, который хранится экземплярами класса AspectJExpressionPointcut.

Кэш каждого экземпляра занимал около 5 МБ.И так как у нас было 3 аспекта и 30 сервисов, это привело к тому, что 90 экземпляров содержали 450 МБ данных.

Изучив содержимое кэша, мы поняли, что он содержит экземпляры метода отражения Java для всех классов, существующих в WARдаже те, которые не являются частью пакета my.package.service.business.После изменения выражения среза точки дополнительно добавьте within предложение:

@Pointcut("execution(public * my.package.service.business.*BusinessServiceImpl.*(..)) && 
within(my.package.service.business..*)")
  public void methodsToBeLogged() {
  }

Использование памяти снова нормализовалось.И все экземпляры AspectJExpressionPointcut заняли менее 1 МБ вместе.

Может кто-нибудь объяснить, почему это так?И почему первой точки среза выражения недостаточно?Почему кеш AspectJExpressionPointcut не является общим?

1 Ответ

9 голосов
/ 10 ноября 2010

AspectJExpressionPointcut использует кэш (shadowMatchCache), который ускоряет решение о том, следует ли применять AOP к определенному вызову метода или нет, на основе выражения pointcut. Этот кэш, возможно, занимает много памяти.

Кроме того, прежде чем предлагать все методы определенного компонента для проверки соответствия выражения pointcut или нет, Spring сначала проверяет, может ли класс компонента соответствовать или нет, вызывая AspectJExpressionPointcut.matches. (Класс targetClass). Этот метод делегирует методу PointcutExpressionImpl.couldPossblyMatch () AspectJ. Это выполнит быструю проверку, может ли класс «возможно» соответствовать выражению pointcut или никогда не будет «точно» соответствовать. По словам разработчиков AspectJ, использующих внутри pointcut, приводит к более определенным нет. Они также рекомендуют никогда не использовать автономные виды командных нажатий (выполнение, вызов, получение, установка), но комбинировать их с .

Однако shadowMatchCache нельзя использовать совместно, поскольку он содержит результат совпадения или отсутствие совпадения для каждого выражения pointcut.

Но, по крайней мере, вы можете ограничить то, что кэшируется. Я также думаю, что Spring мог бы улучшить это, если бы не запустил весь этот кеш, как только будет запущен applicationContext. F.E. они могли бы отбросить все несоответствия за счет повторения некоторых соответствий, когда новый бин динамически добавляется в applicationContext после того, как он уже запущен.

Еще одна возможная проблема с памятью внутри класса AspectJExpressionPointcut - это pointCutParser. Этот синтаксический анализатор может быть общим для всех AspectJExpressionPointcuts в applicationContext. Возьмите добычу на билете JIRA SPR-7678 .

...