Skip to content

Commit 55a43d3

Browse files
artembilangaryrussell
authored andcommitted
Fix memory leak in MHistorConfig & MBeanExpHelper (#2531)
* Fix memory leak in MHistorConfig & MBeanExpHelper The `MessageHistoryConfigurer` is a `BeanPostProcessor` which keeps a store of the `TrackableComponent`. Something similar we have with the `MBeanExporterHelper` and its local stores populated by the `postProcessBeforeInitialization()` * Implement `DestructionAwareBeanPostProcessor` in the `MessageHistoryConfigurer` and `MBeanExporterHelper` to remove destroyed beans from their caches **Cherry-pick to 5.0.x** * * Fix concurrency and checkstyle * Apply Java 8 style for collections iterations
1 parent adbbca1 commit 55a43d3

File tree

2 files changed

+51
-27
lines changed

2 files changed

+51
-27
lines changed

spring-integration-core/src/main/java/org/springframework/integration/history/MessageHistoryConfigurer.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import org.springframework.beans.factory.BeanFactoryAware;
3030
import org.springframework.beans.factory.BeanFactoryUtils;
3131
import org.springframework.beans.factory.ListableBeanFactory;
32-
import org.springframework.beans.factory.config.BeanPostProcessor;
32+
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
3333
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
3434
import org.springframework.context.SmartLifecycle;
3535
import org.springframework.integration.support.management.IntegrationManagedResource;
@@ -50,13 +50,13 @@
5050
*/
5151
@ManagedResource
5252
@IntegrationManagedResource
53-
public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAware, BeanPostProcessor {
53+
public class MessageHistoryConfigurer implements SmartLifecycle, BeanFactoryAware, DestructionAwareBeanPostProcessor {
5454

5555
private final Log logger = LogFactory.getLog(this.getClass());
5656

5757
private final Set<TrackableComponent> currentlyTrackedComponents = ConcurrentHashMap.newKeySet();
5858

59-
private String[] componentNamePatterns = new String[] { "*" };
59+
private String[] componentNamePatterns = new String[]{ "*" };
6060

6161
private boolean componentNamePatternsExplicitlySet;
6262

@@ -98,7 +98,7 @@ public void setComponentNamePatterns(String[] componentNamePatterns) {
9898
*/
9999
@ManagedAttribute(description = "comma-delimited list of patterns; must invoke stop() before changing.")
100100
public void setComponentNamePatternsString(String componentNamePatterns) {
101-
this.setComponentNamePatterns(StringUtils.delimitedListToStringArray(componentNamePatterns, ",", " "));
101+
setComponentNamePatterns(StringUtils.delimitedListToStringArray(componentNamePatterns, ",", " "));
102102
}
103103

104104
@ManagedAttribute
@@ -160,6 +160,16 @@ private void trackComponentIfAny(TrackableComponent component) {
160160
}
161161
}
162162

163+
@Override
164+
public boolean requiresDestruction(Object bean) {
165+
return bean instanceof TrackableComponent;
166+
}
167+
168+
@Override
169+
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
170+
this.currentlyTrackedComponents.remove(bean);
171+
}
172+
163173
/*
164174
* SmartLifecycle implementation
165175
*/

spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,16 +16,17 @@
1616

1717
package org.springframework.integration.jmx.config;
1818

19-
import java.util.ArrayList;
2019
import java.util.Collection;
21-
import java.util.HashSet;
22-
import java.util.List;
20+
import java.util.Queue;
2321
import java.util.Set;
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ConcurrentLinkedQueue;
24+
import java.util.function.Consumer;
2425

2526
import org.springframework.aop.support.AopUtils;
2627
import org.springframework.beans.BeansException;
2728
import org.springframework.beans.DirectFieldAccessor;
28-
import org.springframework.beans.factory.config.BeanPostProcessor;
29+
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
2930
import org.springframework.core.Ordered;
3031
import org.springframework.core.annotation.AnnotatedElementUtils;
3132
import org.springframework.integration.monitor.IntegrationMBeanExporter;
@@ -40,28 +41,29 @@
4041
*
4142
* @author Oleg Zhurakousky
4243
* @author Artem Bilan
44+
*
4345
* @since 2.1
4446
*
4547
*/
46-
class MBeanExporterHelper implements BeanPostProcessor, Ordered {
48+
class MBeanExporterHelper implements DestructionAwareBeanPostProcessor, Ordered {
4749

48-
private final List<MBeanExporter> mBeanExportersForExcludes = new ArrayList<MBeanExporter>();
50+
private final Queue<MBeanExporter> mBeanExportersForExcludes = new ConcurrentLinkedQueue<>();
4951

50-
private final Set<String> siBeanNames = new HashSet<String>();
52+
private final Set<String> siBeanNames = ConcurrentHashMap.newKeySet();
5153

5254
@Override
5355
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
5456
if ("$autoCreateChannelCandidates".equals(beanName)) {
5557
@SuppressWarnings("unchecked")
56-
Collection<String> autoCreateChannelCandidatesNames = (Collection<String>) new DirectFieldAccessor(bean)
57-
.getPropertyValue("channelNames");
58+
Collection<String> autoCreateChannelCandidatesNames =
59+
(Collection<String>) new DirectFieldAccessor(bean).getPropertyValue("channelNames");
5860
this.siBeanNames.addAll(autoCreateChannelCandidatesNames);
5961
if (!this.mBeanExportersForExcludes.isEmpty()) {
60-
for (String autoCreateChannelCandidatesName : autoCreateChannelCandidatesNames) {
61-
for (MBeanExporter mBeanExporter : this.mBeanExportersForExcludes) {
62-
mBeanExporter.addExcludedBean(autoCreateChannelCandidatesName);
63-
}
64-
}
62+
autoCreateChannelCandidatesNames
63+
.stream().
64+
<Consumer<? super MBeanExporter>>map(candidateName ->
65+
mBeanExporter -> mBeanExporter.addExcludedBean(candidateName))
66+
.forEach(this.mBeanExportersForExcludes::forEach);
6567
}
6668
}
6769

@@ -72,25 +74,37 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
7274
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
7375
if (AnnotatedElementUtils.isAnnotated(AopUtils.getTargetClass(bean),
7476
IntegrationManagedResource.class.getName())) {
77+
7578
this.siBeanNames.add(beanName);
76-
if (!this.mBeanExportersForExcludes.isEmpty()) {
77-
for (MBeanExporter mBeanExporter : this.mBeanExportersForExcludes) {
78-
mBeanExporter.addExcludedBean(beanName);
79-
}
80-
}
79+
this.mBeanExportersForExcludes.forEach(mBeanExporter -> mBeanExporter.addExcludedBean(beanName));
8180
}
8281

8382
if (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) {
8483
MBeanExporter mBeanExporter = (MBeanExporter) bean;
8584
this.mBeanExportersForExcludes.add(mBeanExporter);
86-
for (String siBeanName : this.siBeanNames) {
87-
mBeanExporter.addExcludedBean(siBeanName);
88-
}
85+
this.siBeanNames.forEach(mBeanExporter::addExcludedBean);
8986
}
9087

9188
return bean;
9289
}
9390

91+
@Override
92+
public boolean requiresDestruction(Object bean) {
93+
return (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) ||
94+
AnnotatedElementUtils.isAnnotated(AopUtils.getTargetClass(bean),
95+
IntegrationManagedResource.class.getName());
96+
}
97+
98+
@Override
99+
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
100+
if (bean instanceof MBeanExporter) {
101+
this.mBeanExportersForExcludes.remove(bean);
102+
}
103+
else {
104+
this.siBeanNames.remove(beanName);
105+
}
106+
}
107+
94108
@Override
95109
public int getOrder() {
96110
return Ordered.HIGHEST_PRECEDENCE;

0 commit comments

Comments
 (0)