platform: Improve OOM killer for iOS
This commit is contained in:
@@ -409,7 +409,7 @@ func (s *StartedService) SubscribeStatus(request *SubscribeStatusRequest, server
|
|||||||
|
|
||||||
func (s *StartedService) readStatus() *Status {
|
func (s *StartedService) readStatus() *Status {
|
||||||
var status Status
|
var status Status
|
||||||
status.Memory = memory.Inuse()
|
status.Memory = memory.Total()
|
||||||
status.Goroutines = int32(runtime.NumGoroutine())
|
status.Goroutines = int32(runtime.NumGoroutine())
|
||||||
s.serviceAccess.RLock()
|
s.serviceAccess.RLock()
|
||||||
nowService := s.instance
|
nowService := s.instance
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -33,7 +33,7 @@ require (
|
|||||||
github.com/sagernet/gomobile v0.1.11
|
github.com/sagernet/gomobile v0.1.11
|
||||||
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
github.com/sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4
|
||||||
github.com/sagernet/sing v0.8.0-beta.16
|
github.com/sagernet/sing v0.8.0-beta.16.0.20260227013657-e419e9875a07
|
||||||
github.com/sagernet/sing-mux v0.3.4
|
github.com/sagernet/sing-mux v0.3.4
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.13
|
github.com/sagernet/sing-quic v0.6.0-beta.13
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.8
|
github.com/sagernet/sing-shadowsocks v0.2.8
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -226,8 +226,8 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen
|
|||||||
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4 h1:6qvrUW79S+CrPwWz6cMePXohgjHoKxLo3c+MDhNwc3o=
|
||||||
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
github.com/sagernet/quic-go v0.59.0-sing-box-mod.4/go.mod h1:OqILvS182CyOol5zNNo6bguvOGgXzV459+chpRaUC+4=
|
||||||
github.com/sagernet/sing v0.8.0-beta.16 h1:Fe+6E9VHYky9Mx4cf0ugbZPWDcXRflpAu7JQ5bWXvaA=
|
github.com/sagernet/sing v0.8.0-beta.16.0.20260227013657-e419e9875a07 h1:LQqb+xtR5uqF6bePmJQ3sAToF/kMCjxSnz17HnboXA8=
|
||||||
github.com/sagernet/sing v0.8.0-beta.16/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.8.0-beta.16.0.20260227013657-e419e9875a07/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s=
|
||||||
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
|
||||||
github.com/sagernet/sing-quic v0.6.0-beta.13 h1:umDr6GC5fVbOIoTvqV4544wY61zEN+ObQwVGNP8sX1M=
|
github.com/sagernet/sing-quic v0.6.0-beta.13 h1:umDr6GC5fVbOIoTvqV4544wY61zEN+ObQwVGNP8sX1M=
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
type OOMKillerServiceOptions struct{}
|
import (
|
||||||
|
"github.com/sagernet/sing/common/byteformats"
|
||||||
|
"github.com/sagernet/sing/common/json/badoption"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OOMKillerServiceOptions struct {
|
||||||
|
MemoryLimit *byteformats.MemoryBytes `json:"memory_limit,omitempty"`
|
||||||
|
SafetyMargin *byteformats.MemoryBytes `json:"safety_margin,omitempty"`
|
||||||
|
MinInterval badoption.Duration `json:"min_interval,omitempty"`
|
||||||
|
MaxInterval badoption.Duration `json:"max_interval,omitempty"`
|
||||||
|
ChecksBeforeLimit int `json:"checks_before_limit,omitempty"`
|
||||||
|
}
|
||||||
|
|||||||
51
service/oomkiller/config.go
Normal file
51
service/oomkiller/config.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package oomkiller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildTimerConfig(options option.OOMKillerServiceOptions, memoryLimit uint64, useAvailable bool) (timerConfig, error) {
|
||||||
|
safetyMargin := uint64(defaultSafetyMargin)
|
||||||
|
if options.SafetyMargin != nil && options.SafetyMargin.Value() > 0 {
|
||||||
|
safetyMargin = options.SafetyMargin.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
minInterval := defaultMinInterval
|
||||||
|
if options.MinInterval != 0 {
|
||||||
|
minInterval = time.Duration(options.MinInterval.Build())
|
||||||
|
if minInterval <= 0 {
|
||||||
|
return timerConfig{}, E.New("min_interval must be greater than 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxInterval := defaultMaxInterval
|
||||||
|
if options.MaxInterval != 0 {
|
||||||
|
maxInterval = time.Duration(options.MaxInterval.Build())
|
||||||
|
if maxInterval <= 0 {
|
||||||
|
return timerConfig{}, E.New("max_interval must be greater than 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxInterval < minInterval {
|
||||||
|
return timerConfig{}, E.New("max_interval must be greater than or equal to min_interval")
|
||||||
|
}
|
||||||
|
|
||||||
|
checksBeforeLimit := defaultChecksBeforeLimit
|
||||||
|
if options.ChecksBeforeLimit != 0 {
|
||||||
|
checksBeforeLimit = options.ChecksBeforeLimit
|
||||||
|
if checksBeforeLimit <= 0 {
|
||||||
|
return timerConfig{}, E.New("checks_before_limit must be greater than 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return timerConfig{
|
||||||
|
memoryLimit: memoryLimit,
|
||||||
|
safetyMargin: safetyMargin,
|
||||||
|
minInterval: minInterval,
|
||||||
|
maxInterval: maxInterval,
|
||||||
|
checksBeforeLimit: checksBeforeLimit,
|
||||||
|
useAvailable: useAvailable,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -59,35 +59,70 @@ type Service struct {
|
|||||||
boxService.Adapter
|
boxService.Adapter
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
|
memoryLimit uint64
|
||||||
|
hasTimerMode bool
|
||||||
|
useAvailable bool
|
||||||
|
timerConfig timerConfig
|
||||||
|
adaptiveTimer *adaptiveTimer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
||||||
return &Service{
|
s := &Service{
|
||||||
Adapter: boxService.NewAdapter(boxConstant.TypeOOMKiller, tag),
|
Adapter: boxService.NewAdapter(boxConstant.TypeOOMKiller, tag),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
router: service.FromContext[adapter.Router](ctx),
|
router: service.FromContext[adapter.Router](ctx),
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if options.MemoryLimit != nil {
|
||||||
|
s.memoryLimit = options.MemoryLimit.Value()
|
||||||
|
if s.memoryLimit > 0 {
|
||||||
|
s.hasTimerMode = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := buildTimerConfig(options, s.memoryLimit, s.useAvailable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.timerConfig = config
|
||||||
|
|
||||||
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Start(stage adapter.StartStage) error {
|
func (s *Service) Start(stage adapter.StartStage) error {
|
||||||
if stage != adapter.StartStateStart {
|
if stage != adapter.StartStateStart {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.hasTimerMode {
|
||||||
|
s.adaptiveTimer = newAdaptiveTimer(s.logger, s.router, s.timerConfig)
|
||||||
|
if s.memoryLimit > 0 {
|
||||||
|
s.logger.Info("started memory monitor with limit: ", s.memoryLimit/(1024*1024), " MiB")
|
||||||
|
} else {
|
||||||
|
s.logger.Info("started memory monitor with available memory detection")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.logger.Info("started memory pressure monitor")
|
||||||
|
}
|
||||||
|
|
||||||
globalAccess.Lock()
|
globalAccess.Lock()
|
||||||
isFirst := len(globalServices) == 0
|
isFirst := len(globalServices) == 0
|
||||||
globalServices = append(globalServices, s)
|
globalServices = append(globalServices, s)
|
||||||
globalAccess.Unlock()
|
globalAccess.Unlock()
|
||||||
|
|
||||||
if isFirst {
|
if isFirst {
|
||||||
C.startMemoryPressureMonitor()
|
C.startMemoryPressureMonitor()
|
||||||
}
|
}
|
||||||
s.logger.Info("started memory pressure monitor")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Close() error {
|
func (s *Service) Close() error {
|
||||||
|
if s.adaptiveTimer != nil {
|
||||||
|
s.adaptiveTimer.stop()
|
||||||
|
}
|
||||||
globalAccess.Lock()
|
globalAccess.Lock()
|
||||||
for i, service := range globalServices {
|
for i, svc := range globalServices {
|
||||||
if service == s {
|
if svc == s {
|
||||||
globalServices = append(globalServices[:i], globalServices[i+1:]...)
|
globalServices = append(globalServices[:i], globalServices[i+1:]...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -122,17 +157,36 @@ func goMemoryPressureCallback(status C.ulong) {
|
|||||||
default:
|
default:
|
||||||
level = "normal"
|
level = "normal"
|
||||||
}
|
}
|
||||||
|
var freeOSMemory bool
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
|
usage := memory.Total()
|
||||||
|
if s.hasTimerMode {
|
||||||
if isCritical {
|
if isCritical {
|
||||||
s.logger.Error("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB, resetting network")
|
s.logger.Warn("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB")
|
||||||
s.router.ResetNetwork()
|
if s.adaptiveTimer != nil {
|
||||||
|
s.adaptiveTimer.startNow()
|
||||||
|
}
|
||||||
} else if isWarning {
|
} else if isWarning {
|
||||||
s.logger.Warn("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB")
|
s.logger.Warn("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB")
|
||||||
} else {
|
} else {
|
||||||
s.logger.Debug("memory pressure: ", level, ", usage: ", memory.Total()/(1024*1024), " MiB")
|
s.logger.Debug("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB")
|
||||||
|
if s.adaptiveTimer != nil {
|
||||||
|
s.adaptiveTimer.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if isCritical {
|
if isCritical {
|
||||||
|
s.logger.Error("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB, resetting network")
|
||||||
|
s.router.ResetNetwork()
|
||||||
|
freeOSMemory = true
|
||||||
|
} else if isWarning {
|
||||||
|
s.logger.Warn("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB")
|
||||||
|
} else {
|
||||||
|
s.logger.Debug("memory pressure: ", level, ", usage: ", usage/(1024*1024), " MiB")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if freeOSMemory {
|
||||||
runtimeDebug.FreeOSMemory()
|
runtimeDebug.FreeOSMemory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,33 +7,75 @@ import (
|
|||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
boxService "github.com/sagernet/sing-box/adapter/service"
|
boxService "github.com/sagernet/sing-box/adapter/service"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
boxConstant "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/memory"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterService(registry *boxService.Registry) {
|
func RegisterService(registry *boxService.Registry) {
|
||||||
boxService.Register[option.OOMKillerServiceOptions](registry, C.TypeOOMKiller, NewService)
|
boxService.Register[option.OOMKillerServiceOptions](registry, boxConstant.TypeOOMKiller, NewService)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
boxService.Adapter
|
boxService.Adapter
|
||||||
|
logger log.ContextLogger
|
||||||
|
router adapter.Router
|
||||||
|
adaptiveTimer *adaptiveTimer
|
||||||
|
timerConfig timerConfig
|
||||||
|
hasTimerMode bool
|
||||||
|
useAvailable bool
|
||||||
|
memoryLimit uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.OOMKillerServiceOptions) (adapter.Service, error) {
|
||||||
return &Service{
|
s := &Service{
|
||||||
Adapter: boxService.NewAdapter(C.TypeOOMKiller, tag),
|
Adapter: boxService.NewAdapter(boxConstant.TypeOOMKiller, tag),
|
||||||
}, nil
|
logger: logger,
|
||||||
|
router: service.FromContext[adapter.Router](ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.MemoryLimit != nil {
|
||||||
|
s.memoryLimit = options.MemoryLimit.Value()
|
||||||
|
}
|
||||||
|
if s.memoryLimit > 0 {
|
||||||
|
s.hasTimerMode = true
|
||||||
|
} else if memory.AvailableSupported() {
|
||||||
|
s.useAvailable = true
|
||||||
|
s.hasTimerMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := buildTimerConfig(options, s.memoryLimit, s.useAvailable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.timerConfig = config
|
||||||
|
|
||||||
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Start(stage adapter.StartStage) error {
|
func (s *Service) Start(stage adapter.StartStage) error {
|
||||||
if stage != adapter.StartStateStart {
|
if stage != adapter.StartStateStart {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return E.New("memory pressure monitoring is not available on this platform")
|
if !s.hasTimerMode {
|
||||||
|
return E.New("memory pressure monitoring is not available on this platform without memory_limit")
|
||||||
|
}
|
||||||
|
s.adaptiveTimer = newAdaptiveTimer(s.logger, s.router, s.timerConfig)
|
||||||
|
s.adaptiveTimer.start(0)
|
||||||
|
if s.useAvailable {
|
||||||
|
s.logger.Info("started memory monitor with available memory detection")
|
||||||
|
} else {
|
||||||
|
s.logger.Info("started memory monitor with limit: ", s.memoryLimit/(1024*1024), " MiB")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Close() error {
|
func (s *Service) Close() error {
|
||||||
|
if s.adaptiveTimer != nil {
|
||||||
|
s.adaptiveTimer.stop()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
158
service/oomkiller/service_timer.go
Normal file
158
service/oomkiller/service_timer.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package oomkiller
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtimeDebug "runtime/debug"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
"github.com/sagernet/sing/common/memory"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultChecksBeforeLimit = 4
|
||||||
|
defaultMinInterval = 500 * time.Millisecond
|
||||||
|
defaultMaxInterval = 10 * time.Second
|
||||||
|
defaultSafetyMargin = 5 * 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
type adaptiveTimer struct {
|
||||||
|
logger log.ContextLogger
|
||||||
|
router adapter.Router
|
||||||
|
memoryLimit uint64
|
||||||
|
safetyMargin uint64
|
||||||
|
minInterval time.Duration
|
||||||
|
maxInterval time.Duration
|
||||||
|
checksBeforeLimit int
|
||||||
|
useAvailable bool
|
||||||
|
|
||||||
|
access sync.Mutex
|
||||||
|
timer *time.Timer
|
||||||
|
previousUsage uint64
|
||||||
|
lastInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type timerConfig struct {
|
||||||
|
memoryLimit uint64
|
||||||
|
safetyMargin uint64
|
||||||
|
minInterval time.Duration
|
||||||
|
maxInterval time.Duration
|
||||||
|
checksBeforeLimit int
|
||||||
|
useAvailable bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAdaptiveTimer(logger log.ContextLogger, router adapter.Router, config timerConfig) *adaptiveTimer {
|
||||||
|
return &adaptiveTimer{
|
||||||
|
logger: logger,
|
||||||
|
router: router,
|
||||||
|
memoryLimit: config.memoryLimit,
|
||||||
|
safetyMargin: config.safetyMargin,
|
||||||
|
minInterval: config.minInterval,
|
||||||
|
maxInterval: config.maxInterval,
|
||||||
|
checksBeforeLimit: config.checksBeforeLimit,
|
||||||
|
useAvailable: config.useAvailable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) start(_ uint64) {
|
||||||
|
t.access.Lock()
|
||||||
|
defer t.access.Unlock()
|
||||||
|
t.startLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) startNow() {
|
||||||
|
t.access.Lock()
|
||||||
|
t.startLocked()
|
||||||
|
t.access.Unlock()
|
||||||
|
t.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) startLocked() {
|
||||||
|
if t.timer != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.previousUsage = memory.Total()
|
||||||
|
t.lastInterval = t.minInterval
|
||||||
|
t.timer = time.AfterFunc(t.minInterval, t.poll)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) stop() {
|
||||||
|
t.access.Lock()
|
||||||
|
defer t.access.Unlock()
|
||||||
|
t.stopLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) stopLocked() {
|
||||||
|
if t.timer != nil {
|
||||||
|
t.timer.Stop()
|
||||||
|
t.timer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) running() bool {
|
||||||
|
t.access.Lock()
|
||||||
|
defer t.access.Unlock()
|
||||||
|
return t.timer != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *adaptiveTimer) poll() {
|
||||||
|
t.access.Lock()
|
||||||
|
defer t.access.Unlock()
|
||||||
|
if t.timer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
usage := memory.Total()
|
||||||
|
delta := int64(usage) - int64(t.previousUsage)
|
||||||
|
t.previousUsage = usage
|
||||||
|
|
||||||
|
var remaining uint64
|
||||||
|
var triggered bool
|
||||||
|
|
||||||
|
if t.memoryLimit > 0 {
|
||||||
|
if usage >= t.memoryLimit {
|
||||||
|
remaining = 0
|
||||||
|
triggered = true
|
||||||
|
} else {
|
||||||
|
remaining = t.memoryLimit - usage
|
||||||
|
}
|
||||||
|
} else if t.useAvailable {
|
||||||
|
available := memory.Available()
|
||||||
|
if available <= t.safetyMargin {
|
||||||
|
remaining = 0
|
||||||
|
triggered = true
|
||||||
|
} else {
|
||||||
|
remaining = available - t.safetyMargin
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
remaining = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if triggered {
|
||||||
|
t.logger.Error("memory threshold reached, usage: ", usage/(1024*1024), " MiB, resetting network")
|
||||||
|
t.router.ResetNetwork()
|
||||||
|
runtimeDebug.FreeOSMemory()
|
||||||
|
}
|
||||||
|
|
||||||
|
var interval time.Duration
|
||||||
|
if triggered {
|
||||||
|
interval = t.maxInterval
|
||||||
|
} else if delta <= 0 {
|
||||||
|
interval = t.maxInterval
|
||||||
|
} else if t.checksBeforeLimit <= 0 {
|
||||||
|
interval = t.maxInterval
|
||||||
|
} else {
|
||||||
|
timeToLimit := time.Duration(float64(remaining) / float64(delta) * float64(t.lastInterval))
|
||||||
|
interval = timeToLimit / time.Duration(t.checksBeforeLimit)
|
||||||
|
if interval < t.minInterval {
|
||||||
|
interval = t.minInterval
|
||||||
|
}
|
||||||
|
if interval > t.maxInterval {
|
||||||
|
interval = t.maxInterval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.lastInterval = interval
|
||||||
|
t.timer.Reset(interval)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user