Hi alt,
We discovered a memory leak in our production environment. Memory grows from ~2GB to 3.6GB over one week and never releases.
Environment:
- Inetlab.SMPP version: 2.9.35
- .NET 8
- Linux
Memory profiler shows DiagNode<ActivityEvent> objects accumulating: see image1.jpeg
Memory growth over time: see image2.jpeg
Root Cause Analysis:
Found issue in SmppSession.cs - Activity objects not disposed for throttled requests.
Line 960 - throttled path:
_ = SendResponseAndDelayedRequests(resp, null, activity, _cancellationTokenSource.Token);
Activity is created at line 933 but SendResponseAndDelayedRequests never disposes it. Only HandleReceivedRequest disposes activity at line 718, but throttled requests bypass this method.
Can you please review and confirm?
Thank you.
Memory Leak - Activity objects not disposed in SmppSession.cs
Memory Leak - Activity objects not disposed in SmppSession.cs
- Attachments
-
- image2.jpg (219.09 KiB) Viewed 46 times
-
- image1.jpeg (234.2 KiB) Viewed 46 times
Last edited by ggsa on Sat Nov 29, 2025 7:36 am, edited 1 time in total.
Re: Memory Leak - Activity objects not disposed in SmppSession.cs
Update: Possible root cause for our memory leak case - "Activity Theft" in SmppConnection
After deeper look into dotMemory, seems we found the cause.
Profiler Data:
- Activity objects: 49
- DiagNode: 5,158,820 (thousands events per Activity!)
- SmppConnection: 765
- SmppSession: 765
Retention path:
DiagNode<ActivityEvent> → Activity → ExecutionContext
→ AsyncStateMachineBox<SmppConnection+<Transferring>d__63>
→ _transferTask → SmppConnection
Potential bug:
SmppConnection constructor (line 182):
_transferTask = Transferring();
This captures current ExecutionContext. If Activity.Current has value at this moment (e.g., during connection recovery triggered while processing deliver_sm), the Activity might get "stolen" by Transferring() task.
Since Transferring() runs for entire connection lifetime, all subsequent PDU operations call Activity.Current?.AddEvent() (line 487) and add events to this captured Activity.
We have many bindings on this node. Some providers are unstable ("bad actors") and connection recovery is normal situation. When recovery happens during active deliver_sm/submit_sm processing, new SmppConnection captures the Activity from that context.
Result: 49 "contaminated" connections accumulated 5+ million events over one week.
Can you confirm this analysis?
After deeper look into dotMemory, seems we found the cause.
Profiler Data:
- Activity objects: 49
- DiagNode: 5,158,820 (thousands events per Activity!)
- SmppConnection: 765
- SmppSession: 765
Retention path:
DiagNode<ActivityEvent> → Activity → ExecutionContext
→ AsyncStateMachineBox<SmppConnection+<Transferring>d__63>
→ _transferTask → SmppConnection
Potential bug:
SmppConnection constructor (line 182):
_transferTask = Transferring();
This captures current ExecutionContext. If Activity.Current has value at this moment (e.g., during connection recovery triggered while processing deliver_sm), the Activity might get "stolen" by Transferring() task.
Since Transferring() runs for entire connection lifetime, all subsequent PDU operations call Activity.Current?.AddEvent() (line 487) and add events to this captured Activity.
We have many bindings on this node. Some providers are unstable ("bad actors") and connection recovery is normal situation. When recovery happens during active deliver_sm/submit_sm processing, new SmppConnection captures the Activity from that context.
Result: 49 "contaminated" connections accumulated 5+ million events over one week.
Can you confirm this analysis?