Skip to content

Commit 7c5b53e

Browse files
authored
Merge pull request #1186 from microsoft/trask/fix-channel-fetcher-oom
Fix ChannelFetcher memory leak
2 parents 0f344f8 + 369eae9 commit 7c5b53e

File tree

4 files changed

+96
-18
lines changed

4 files changed

+96
-18
lines changed

core/src/main/java/com/microsoft/applicationinsights/TelemetryClient.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@
4949
* General overview https://docs.microsoft.com/azure/application-insights/app-insights-api-custom-events-metrics
5050
*/
5151
public class TelemetryClient {
52-
private final class TelemetryClientChannelFetcher implements ChannelFetcher {
53-
public TelemetryChannel fetch() {
54-
return getChannel();
55-
}
56-
}
5752

5853
private final TelemetryConfiguration configuration;
5954
private volatile TelemetryContext context;
@@ -73,7 +68,7 @@ public TelemetryClient(TelemetryConfiguration configuration) {
7368
}
7469

7570
synchronized (TELEMETRY_STOP_HOOK_LOCK) {
76-
SDKShutdownActivity.INSTANCE.register(new TelemetryClientChannelFetcher());
71+
SDKShutdownActivity.INSTANCE.register(configuration.getChannel());
7772
}
7873

7974
this.configuration = configuration;

core/src/main/java/com/microsoft/applicationinsights/internal/shutdown/SDKShutdownActivity.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
package com.microsoft.applicationinsights.internal.shutdown;
2323

2424
import java.io.Closeable;
25+
import java.util.HashMap;
2526
import java.util.List;
2627
import java.util.ArrayList;
28+
import java.util.Map;
29+
import java.util.WeakHashMap;
2730
import java.util.concurrent.ExecutorService;
2831
import java.util.concurrent.TimeUnit;
2932

@@ -51,10 +54,16 @@ public enum SDKShutdownActivity {
5154
private static class SDKShutdownAction implements Runnable {
5255
private boolean stopped = false;
5356

57+
private final Map<TelemetryChannel, Boolean> channels = new HashMap<>();
58+
@Deprecated
5459
private final List<ChannelFetcher> fetchers = new ArrayList<ChannelFetcher>();
5560
private final List<Stoppable> stoppables = new ArrayList<Stoppable>();
5661
private final List<Closeable> closeables = new ArrayList<Closeable>();
5762

63+
public synchronized void register(TelemetryChannel channel) {
64+
channels.put(channel, true);
65+
}
66+
5867
public synchronized void register(ChannelFetcher fetcher) {
5968
fetchers.add(fetcher);
6069
}
@@ -126,23 +135,30 @@ private void stopInternalLogger() {
126135
* Make sure no exception is thrown!
127136
*/
128137
private void stopChannels() {
138+
for (TelemetryChannel channel : channels.keySet()) {
139+
stopChannel(channel);
140+
}
129141
for (ChannelFetcher fetcher : fetchers) {
142+
stopChannel(fetcher.fetch());
143+
}
144+
}
145+
146+
private void stopChannel(TelemetryChannel channelToStop) {
147+
try {
148+
if (channelToStop != null) {
149+
channelToStop.stop(getPerThreadTimeout(), getPerThreadTimeUnit());
150+
}
151+
} catch (ThreadDeath td) {
152+
throw td;
153+
} catch (Throwable t) {
130154
try {
131-
TelemetryChannel channelToStop = fetcher.fetch();
132-
if (channelToStop != null) {
133-
channelToStop.stop(getPerThreadTimeout(), getPerThreadTimeUnit());
155+
if (InternalLogger.INSTANCE.isErrorEnabled()) {
156+
InternalLogger.INSTANCE.error("Failed to stop channel: '%s'", ExceptionUtils.getStackTrace(t));
134157
}
135158
} catch (ThreadDeath td) {
136159
throw td;
137-
} catch (Throwable t) {
138-
try {
139-
InternalLogger.INSTANCE.error("Failed to stop channel: '%s'", t.toString());
140-
InternalLogger.INSTANCE.trace("Stack trace generated is %s", ExceptionUtils.getStackTrace(t));
141-
} catch (ThreadDeath td) {
142-
throw td;
143-
} catch (Throwable t2) {
144-
// chomp
145-
}
160+
} catch (Throwable t2) {
161+
// chomp
146162
}
147163
}
148164
}
@@ -190,6 +206,10 @@ private void closeClosables() {
190206

191207
private static volatile SDKShutdownAction shutdownAction;
192208

209+
public void register(TelemetryChannel channel) {
210+
getShutdownAction().register(channel);
211+
}
212+
193213
public void register(ChannelFetcher fetcher) {
194214
getShutdownAction().register(fetcher);
195215
}

core/src/main/java/com/microsoft/applicationinsights/internal/util/ChannelFetcher.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
/**
2727
* Created by gupele on 2/2/2015.
2828
*/
29+
@Deprecated
2930
public interface ChannelFetcher {
3031
TelemetryChannel fetch();
3132
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* ApplicationInsights-Java
3+
* Copyright (c) Microsoft Corporation
4+
* All rights reserved.
5+
*
6+
* MIT License
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
8+
* software and associated documentation files (the ""Software""), to deal in the Software
9+
* without restriction, including without limitation the rights to use, copy, modify, merge,
10+
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit
11+
* persons to whom the Software is furnished to do so, subject to the following conditions:
12+
* The above copyright notice and this permission notice shall be included in all copies or
13+
* substantial portions of the Software.
14+
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16+
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17+
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
* DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
package com.microsoft.applicationinsights;
23+
24+
import java.util.LinkedList;
25+
import java.util.List;
26+
27+
import com.microsoft.applicationinsights.channel.TelemetryChannel;
28+
import com.microsoft.applicationinsights.telemetry.Telemetry;
29+
import org.junit.*;
30+
import org.mockito.Matchers;
31+
import org.mockito.Mockito;
32+
import org.mockito.invocation.InvocationOnMock;
33+
import org.mockito.stubbing.Answer;
34+
35+
import static org.mockito.Mockito.mock;
36+
37+
public final class TelemetryClientMemoryTest {
38+
39+
@Test
40+
public void shouldNotRunOOM() {
41+
TelemetryConfiguration configuration = new TelemetryConfiguration();
42+
configuration.setInstrumentationKey("00000000-0000-0000-0000-000000000000");
43+
TelemetryChannel channel = mock(TelemetryChannel.class);
44+
configuration.setChannel(channel);
45+
46+
final List<Telemetry> eventsSent = new LinkedList<Telemetry>();
47+
// Setting the channel to add the sent telemetries to a collection, so they could be verified in tests.
48+
Mockito.doAnswer(new Answer() {
49+
@Override
50+
public Object answer(InvocationOnMock invocation) throws Throwable {
51+
Telemetry telemetry = ((Telemetry) invocation.getArguments()[0]);
52+
eventsSent.add(telemetry);
53+
54+
return null;
55+
}
56+
}).when(channel).send(Matchers.any(Telemetry.class));
57+
58+
for (int i = 0; i < 100000000; i++) {
59+
new TelemetryClient(configuration).getChannel();
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)