SignalR Java Client 9.0.4: Memory leak in stream() method due to unbounded ReplaySubject
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
### Describe the bug
The SignalR Java client version 9.0.4 has a memory leak when using the `stream()` method for real-time data streaming. The issue is caused by the use of unbounded `ReplaySubject.create()` in two places:
1. `InvocationRequest.java:14` - `private final Subject pendingCall = ReplaySubject.create();`
2. `HubConnection.java:796` - `ReplaySubject subject = ReplaySubject.create();` (in the `stream()` method)
These unbounded replay subjects cache ALL emitted items indefinitely, causing memory to grow without bounds during long-running WebSocket connections with continuous data streams.
### To Reproduce
Steps to reproduce the behavior:
1. Create a SignalR Java client connection
2. Use the `stream()` method to subscribe to a high-frequency data stream (e.g., real-time trading data)
3. Keep the connection running for extended periods (>1 hour)
4. Monitor memory usage - it will continuously grow until OutOfMemoryError occurs
Example code:
```java
HubConnection hubConnection = HubConnectionBuilder.create("wss://example.com/hub")
.withAccessTokenProvider(Single.just(accessToken))
.build();
hubConnection.start().blockingAwait();
// This will cause memory leak over time
Observable stream = hubConnection.stream(MarketData.class, "StreamMarketData", "symbol");
stream.subscribe(data -> {
// Process real-time data
});
```
### Expected behavior
The streaming connection should maintain stable memory usage over time, not accumulating all historical data in memory.
### Actual behavior
Memory usage grows continuously as `ReplaySubject` caches all emitted items. This leads to OutOfMemoryError after extended periods of streaming.
### Workaround
Currently, we're forced to periodically disconnect and reconnect the SignalR connection every 5-15 minutes to clear the internal buffers and prevent OOM errors.
### Suggested Fix
Replace unbounded `ReplaySubject.create()` with either:
1. Bounded replay: `ReplaySubject.createWithSize(bufferSize)`
2. PublishSubject for no caching: `PublishSubject.create()`
3. Make buffer size configurable
### Environment
- SignalR Java Client version: 9.0.4
- Java version: 11/17
- Platform: Android
- RxJava version: 3.x
### Stack trace
```
java.lang.OutOfMemoryError: Failed to allocate a 24 byte allocation with 1349584 free bytes and 1317KB until OOM
at com.android.org.conscrypt.ConscryptEngine.newResult(ConscryptEngine.java:1363)
at com.android.org.conscrypt.ConscryptEngine.unwrap(ConscryptEngine.java:933)
...
at okhttp3.internal.ws.WebSocketReader.processNextFrame(WebSocketReader.kt:0)
at okhttp3.internal.ws.RealWebSocket.loopReader(RealWebSocket.kt:293)
```
### Additional context
This issue affects production applications using SignalR for real-time data streaming, particularly in financial trading applications where data streams are continuous and high-frequency.
### Expected Behavior
_No response_
### Steps To Reproduce
_No response_
### Exceptions (if any)
_No response_
### .NET Version
_No response_
### Anything else?
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.