2010-11-04 17 views
5

En nuestra aplicación tenemos varios (actualmente muchos, unos 30) servicios web. Cada servicio web reside en su propio archivo WAR y tiene su propio contexto Spring que se inicializa cuando se inicia la aplicación.Aspectos que escanean demasiadas clases y la memoria caché de método llena la memoria

También tenemos varias clases de aspectos impulsadas por anotaciones que aplicamos a las clases de servicio web. En el principio la expresión poincut veía así:

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

Y AOP fue habilitada en servicios a través de la entrada en la configuración.

Pero cuando el número de servicios web creció, comenzamos a experimentar OutOfMemoryException s en nuestros servidores. Después de hacer algunos perfiles y análisis, parece que la memoria está ocupada por la memoria caché que mantienen las instancias de la clase AspectJExpressionPointcut.

El caché de cada instancia era de aproximadamente 5 MB. Y como teníamos 3 aspectos y 30 servicios, resultó en 90 instancias con 450 MB de datos en total.

Después de examinar el contenido de la caché nos dimos cuenta de que contiene las instancias método de reflexión de Java para todas las clases existentes en el GUERRA incluso aquellos que no son parte del paquete de my.package.service.business. Después de modifing la expresión punto de corte para tener adicionalmente within cláusula:

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

uso de la memoria se redujo a la normalidad. Y todas las instancias de AspectJExpressionPointcut tomaron menos de 1 MB todos juntos.

¿Alguien puede explicar por qué es eso? ¿Y por qué la expresión del primer punto no es suficiente? ¿Por qué el caché de AspectJExpressionPointcut no se comparte?

Respuesta

7

AspectJExpressionPointcut utiliza una memoria caché (shadowMatchCache) que acelera la decisión de si AOP se debe aplicar a una determinada llamada de método o no, en función de la expresión de corte de punto. Este caché posiblemente consume mucha memoria.

Adicionalmente, antes de ofrecer todos los métodos de un grano específico para ver si hay una coincidencia de expresión punto de corte o no, comprueba en primer lugar la primavera si una clase de frijol, podría posiblemente coincida o no, llamando AspectJExpressionPointcut.matches (Clase targetClass) Este método delega en el método PointcutExpressionImpl.couldPossiblyMatch() de AspectJ. Esto realizará un rápido para verificar si una clase podría 'posiblemente' coincidir con una expresión de corte puntual o nunca 'con precisión' coincidiría. De acuerdo con los desarrolladores de AspectJ que usan un punto de corte interno, da como resultado no más definidos. También recommend to never use a standalone kind of pointcuts (execution, call, get, set), but combine these with within.

shadowMatchCache no se puede compartir, sin embargo, porque contiene el resultado de coincidencia o no coincidencia por expresión de corte de punto.

Pero al menos puede limitar lo que se almacena en caché. También creo que Spring podría mejorar al no mantener todo este caché, una vez que se inicie applicationContext. F.e. posiblemente podrían deshacerse de todas las coincidencias, a expensas de rehacer algunas de las coincidencias, cuando se agrega dinámicamente un nuevo bean al ApplicationContext después de que ya se haya iniciado.

Otro posible problema de memoria dentro de la clase AspectJExpressionPointcut es el pointCutParser. Este analizador podría compartirse en todos los AspectJExpressionPointcuts en el applicationContext. Tome un botín en el boleto de JIRA SPR-7678.

+0

¡Excelente respuesta! –

Cuestiones relacionadas