From b418ee1a98afbefca99593432e3390c183cd7e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 28 Apr 2026 08:18:08 +0800 Subject: [PATCH] Improve oom-killer --- dns/router.go | 3 +++ experimental/clashapi/server.go | 4 ++++ .../clashapi/trafficontrol/manager.go | 7 ++++--- service/oomkiller/badcleanup.go | 19 +++++++++++++++++++ service/oomkiller/badcleanup_stub.go | 6 ++++++ service/oomkiller/service_darwin.go | 2 -- service/oomkiller/timer.go | 14 +++++++++----- 7 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 service/oomkiller/badcleanup.go create mode 100644 service/oomkiller/badcleanup_stub.go diff --git a/dns/router.go b/dns/router.go index adde3bac0..c0d681abd 100644 --- a/dns/router.go +++ b/dns/router.go @@ -856,6 +856,9 @@ func (r *Router) ClearCache() { if r.platformInterface != nil { r.platformInterface.ClearDNSCache() } + if r.dnsReverseMapping != nil { + r.dnsReverseMapping.Purge() + } } func (r *Router) LookupReverseMapping(ip netip.Addr) (string, bool) { diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index ec40a95fc..20cea0bf9 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -21,6 +21,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/cleanup" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/json" N "github.com/sagernet/sing/common/network" @@ -51,6 +52,7 @@ type Server struct { trafficManager *trafficontrol.Manager urlTestHistory adapter.URLTestHistoryStorage logDebug bool + cleaner *cleanup.Cleaner mode string modeList []string @@ -82,6 +84,7 @@ func NewServer(ctx context.Context, logFactory log.ObservableFactory, options op externalController: options.ExternalController != "", externalUIDownloadURL: options.ExternalUIDownloadURL, externalUIDownloadDetour: options.ExternalUIDownloadDetour, + cleaner: cleanup.Add(trafficManager.Clear), } s.urlTestHistory = service.FromContext[adapter.URLTestHistoryStorage](ctx) if s.urlTestHistory == nil { @@ -193,6 +196,7 @@ func (s *Server) Close() error { common.PtrOrNil(s.httpServer), s.trafficManager, s.urlTestHistory, + common.PtrOrNil(s.cleaner), ) } diff --git a/experimental/clashapi/trafficontrol/manager.go b/experimental/clashapi/trafficontrol/manager.go index 6763436d8..6bd77def1 100644 --- a/experimental/clashapi/trafficontrol/manager.go +++ b/experimental/clashapi/trafficontrol/manager.go @@ -159,9 +159,10 @@ func (m *Manager) Snapshot() *Snapshot { } } -func (m *Manager) ResetStatistic() { - m.uploadTotal.Store(0) - m.downloadTotal.Store(0) +func (m *Manager) Clear() { + m.closedConnectionsAccess.Lock() + defer m.closedConnectionsAccess.Unlock() + m.closedConnections.Init() } type Snapshot struct { diff --git a/service/oomkiller/badcleanup.go b/service/oomkiller/badcleanup.go new file mode 100644 index 000000000..4ba4b0cef --- /dev/null +++ b/service/oomkiller/badcleanup.go @@ -0,0 +1,19 @@ +//go:build badlinkname + +package oomkiller + +import ( + "sync" + _ "unsafe" +) + +//go:linkname jsonFieldCache json.fieldCache +var jsonFieldCache sync.Map + +//go:linkname contextJSONFieldCache github.com/sagernet/sing/common/json/internal/contextjson.fieldCache +var contextJSONFieldCache sync.Map + +func badCleanup() { + jsonFieldCache.Clear() + contextJSONFieldCache.Clear() +} diff --git a/service/oomkiller/badcleanup_stub.go b/service/oomkiller/badcleanup_stub.go new file mode 100644 index 000000000..d734eb9f1 --- /dev/null +++ b/service/oomkiller/badcleanup_stub.go @@ -0,0 +1,6 @@ +//go:build !badlinkname + +package oomkiller + +func badCleanup() { +} diff --git a/service/oomkiller/service_darwin.go b/service/oomkiller/service_darwin.go index a40daea10..ad3164b83 100644 --- a/service/oomkiller/service_darwin.go +++ b/service/oomkiller/service_darwin.go @@ -33,7 +33,6 @@ static void stopMemoryPressureMonitor() { import "C" import ( - runtimeDebug "runtime/debug" "sync" "github.com/sagernet/sing-box/adapter" @@ -90,7 +89,6 @@ func (s *Service) Close() error { //export goMemoryPressureCallback func goMemoryPressureCallback(status C.ulong) { - runtimeDebug.FreeOSMemory() globalAccess.Lock() services := make([]*Service, len(globalServices)) copy(services, globalServices) diff --git a/service/oomkiller/timer.go b/service/oomkiller/timer.go index a5bef3a71..f2070ce3f 100644 --- a/service/oomkiller/timer.go +++ b/service/oomkiller/timer.go @@ -105,6 +105,7 @@ type adaptiveTimer struct { limitThresholds pressureThresholds access sync.Mutex + cleanupTriggered bool timer *time.Timer state pressureState currentInterval time.Duration @@ -161,10 +162,6 @@ func (t *adaptiveTimer) stop() { } func (t *adaptiveTimer) poll() { - if t.timerConfig.policyMode == policyModeNetworkExtension { - runtimeDebug.FreeOSMemory() - } - var triggered bool var rateTriggered bool sample := readMemorySample(t.policyMode) @@ -174,6 +171,12 @@ func (t *adaptiveTimer) poll() { t.access.Unlock() return } + if t.timerConfig.policyMode == policyModeNetworkExtension { + if t.cleanupTriggered { + runtimeDebug.FreeOSMemory() + t.cleanupTriggered = true + } + } if t.pendingPressureBaseline { t.pressureBaseline = sample t.pressureBaselineTime = time.Now() @@ -205,10 +208,10 @@ func (t *adaptiveTimer) poll() { } } t.access.Unlock() - if !triggered { return } + t.cleanupTriggered = false t.onTriggered(sample.usage) if rateTriggered { if t.killerDisabled { @@ -225,6 +228,7 @@ func (t *adaptiveTimer) poll() { t.router.ResetNetwork() } } + badCleanup() runtimeDebug.FreeOSMemory() }