Skip to content

Fix memory leaks in server disposal#3560

Merged
marcschier merged 1 commit intoOPCFoundation:masterfrom
RicoSuter:fix/disposal-memory-leaks
Feb 17, 2026
Merged

Fix memory leaks in server disposal#3560
marcschier merged 1 commit intoOPCFoundation:masterfrom
RicoSuter:fix/disposal-memory-leaks

Conversation

@RicoSuter
Copy link
Contributor

@RicoSuter RicoSuter commented Feb 16, 2026

Two related disposal bugs prevent the server object graph from being garbage collected after StopAsync/Dispose when clients were connected. Discovered during resilience testing of a long-running OPC UA server with repeated restart cycles (~2 MB leaked per cycle).

Disclaimer: This bug has been found with hours of manual work with AI and my local OPC UA stress test application and should be safe - please review the changes as I don't really know the OPC UA code base in depth.

1. UaSCUaBinaryChannel.Dispose() does not dispose the Socket property

Pending async I/O keeps the socket alive in SocketAsyncEngine, which retains the entire channel/listener graph and prevents GC. Fixed by adding Utils.SilentDispose(Socket) in Dispose.

Double-dispose is safe because TcpMessageSocket.Close() nulls m_socket before disposing, making subsequent Dispose() calls a no-op. TcpServerChannel.Dispose() acquires DataLock, preventing races with ForceChannelFault/SendErrorMessage.

2. StandardServer.OnServerStarted() subscribes CertificateValidator.CertificateUpdate but never unsubscribes

Since the CertificateValidator is shared (from ApplicationConfiguration) and outlives the server, the delegate retains every disposed server instance. Fixed by unsubscribing in StandardServer.Dispose(bool) inside the disposing guard.

GC retention chains (before fix)

SocketAsyncEngine → Socket → TcpMessageSocket → TcpServerChannel
  → TcpTransportListener → m_callback → SessionEndpoint → Server

Socket → Channel → ChannelQuotas → CertificateValidator
  → CertificateUpdateEventHandler → Server

Verification

Tested in a long-running OPC UA server with chaos testing (repeated server stop/start cycles with connected clients):

  • Before fix: ~2 MB/cycle heap growth, retained server instances accumulating
  • After fix: HeapMB stable (75–77 MB range over 22+ cycles), no retained server instances

Notes

  • Both fixes are inside existing Dispose(bool) methods, inside the disposing guard
  • No behavioral changes during normal operation — only affects cleanup after disposal
  • These bugs only manifest when servers are repeatedly created and destroyed within the same process (e.g., server restart loops). In single-lifecycle production deployments, process exit cleans everything up.

@RicoSuter RicoSuter force-pushed the fix/disposal-memory-leaks branch from 20e9725 to d2ea5cd Compare February 16, 2026 22:06
@RicoSuter RicoSuter force-pushed the fix/disposal-memory-leaks branch from d2ea5cd to 3e9e183 Compare February 16, 2026 22:18
@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.68%. Comparing base (d25caff) to head (3e9e183).
⚠️ Report is 121 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #3560       +/-   ##
===========================================
+ Coverage   51.86%   64.68%   +12.81%     
===========================================
  Files         370      458       +88     
  Lines       78618    96776    +18158     
  Branches    13650    16265     +2615     
===========================================
+ Hits        40779    62601    +21822     
+ Misses      33705    29161     -4544     
- Partials     4134     5014      +880     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@marcschier marcschier merged commit 63a77a2 into OPCFoundation:master Feb 17, 2026
93 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments