[tools] gopls/internal/server: add memoryLimit setting

1 view
Skip to first unread message

Alan Donovan (Gerrit)

unread,
Jun 26, 2026, 5:16:22 PM (14 hours ago) Jun 26
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Alan Donovan has uploaded the change for review

Commit message

gopls/internal/server: add memoryLimit setting

This setting imposes a soft limit on memory usage of the gopls
process via the runtime/debug.SetMemoryLimit mechanism.
This feature is experimental. It may permit users to trade
more CPU for lower steady state memory usage; or it may cause
GC thrashing if set too low.

Thanks to Brian Dillmann at Cockroach labs for contributing it.
(See https://github.com/golang/tools/pull/651.)

+ relnote
Change-Id: Idaf6390787a8895336ec27071e67014f4a17982a

Change diff

diff --git a/gopls/doc/release/v0.24.0.md b/gopls/doc/release/v0.24.0.md
new file mode 100644
index 0000000..efa5e04
--- /dev/null
+++ b/gopls/doc/release/v0.24.0.md
@@ -0,0 +1,28 @@
+---
+title: "Gopls release v0.24.0 (expected Sep 2026)"
+---
+
+In this release:
+
+- [commits](https://go.googlesource.com/tools/+log/refs/heads/gopls-release-branch.0.23..refs/heads/gopls-release-branch.0.24)
+- [issues closed](https://github.com/golang/go/milestone/440?closed=1)
+
+Key features are described below.
+
+<!-- TODO: 1-line overview of release -->
+
+## Configuration changes
+
+The new, experimental `memoryLimit` setting allows clients to set a
+soft memory limit on the gopls process using
+[runtime/debug.SetMemoryLimit](https://pkg.go.dev/runtime/debug#SetMemoryLimit).
+
+## Web-based features
+
+## Editing features
+
+## Analysis features
+
+<!-- TODO Gopls is now using staticcheck [v0.8.0-rc1](https://github.com/dominikh/go-tools/releases/tag/2026.2rc1). -->
+
+## Code transformation features
diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md
index 93e3e87..8e0602c 100644
--- a/gopls/doc/settings.md
+++ b/gopls/doc/settings.md
@@ -683,6 +683,31 @@

Default: `0`.

+<a id='memoryLimit'></a>
+### `memoryLimit int64`
+
+**This setting is experimental and may be deleted.**
+
+memoryLimit sets a soft memory limit (in bytes) for the gopls process, via
+runtime/debug.SetMemoryLimit. If non-positive (the default), no limit is set.
+
+On large workspaces, a single edit that invalidates many
+packages (for example a syntax error in a widely-imported
+package) can make the heap briefly grow well above the
+steady-state working set before the garbage collector
+catches up, spiking memory and, on memory-constrained
+machines, causing swapping. A soft limit makes the GC work
+harder to stay near the limit, trading some CPU for a lower
+memory peak.
+
+The limit is soft and may be exceeded. Set it comfortably above the
+steady-state working set, as too low a value causes excessive GC.
+
+Unlike the GOMEMLIMIT environment variable, this setting is
+strictly numeric; SI suffixes are not permitted.
+
+Default: `0`.
+
<a id='verboseOutput'></a>
### `verboseOutput bool`

diff --git a/gopls/internal/doc/api.json b/gopls/internal/doc/api.json
index b9cf154..1db6b07 100644
--- a/gopls/internal/doc/api.json
+++ b/gopls/internal/doc/api.json
@@ -2343,6 +2343,20 @@
"DeprecationMessage": ""
},
{
+ "Name": "memoryLimit",
+ "Type": "int64",
+ "Doc": "memoryLimit sets a soft memory limit (in bytes) for the gopls process, via\nruntime/debug.SetMemoryLimit. If non-positive (the default), no limit is set.\n\nOn large workspaces, a single edit that invalidates many\npackages (for example a syntax error in a widely-imported\npackage) can make the heap briefly grow well above the\nsteady-state working set before the garbage collector\ncatches up, spiking memory and, on memory-constrained\nmachines, causing swapping. A soft limit makes the GC work\nharder to stay near the limit, trading some CPU for a lower\nmemory peak.\n\nThe limit is soft and may be exceeded. Set it comfortably above the\nsteady-state working set, as too low a value causes excessive GC.\n\nUnlike the GOMEMLIMIT environment variable, this setting is\nstrictly numeric; SI suffixes are not permitted.\n",
+ "EnumKeys": {
+ "ValueType": "",
+ "Keys": null
+ },
+ "EnumValues": null,
+ "Default": "0",
+ "Status": "experimental",
+ "Hierarchy": "",
+ "DeprecationMessage": ""
+ },
+ {
"Name": "verboseOutput",
"Type": "bool",
"Doc": "verboseOutput enables additional debug logging.\n",
diff --git a/gopls/internal/protocol/tsjson.go b/gopls/internal/protocol/tsjson.go
index c672b91..3c96bff 100644
--- a/gopls/internal/protocol/tsjson.go
+++ b/gopls/internal/protocol/tsjson.go
@@ -10,10 +10,9 @@
// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.6-next.14/protocol/metaModel.json
// LSP metaData.version = 3.17.0.

-import (
- "encoding/json"
- "fmt"
-)
+import "encoding/json"
+
+import "fmt"

// UnmarshalError indicates that a JSON value did not conform to
// one of the expected cases of an LSP union type.
diff --git a/gopls/internal/server/general.go b/gopls/internal/server/general.go
index 420a702..37846a0 100644
--- a/gopls/internal/server/general.go
+++ b/gopls/internal/server/general.go
@@ -16,6 +16,7 @@
"os"
"path"
"path/filepath"
+ runtimedebug "runtime/debug"
"slices"
"sort"
"strings"
@@ -71,9 +72,7 @@
}
options.ForClientCapabilities(params.ClientInfo, params.Capabilities)

- if options.MaxFileCacheBytes > 0 {
- filecache.SetBudget(options.MaxFileCacheBytes)
- }
+ updateGlobalOptions(options)

if options.ShowBugReports {
// Report the next bug that occurs on the server.
@@ -277,6 +276,17 @@
}, nil
}

+// updateGlobalOptions updates process-wide settings
+// received by a DidChangeConfiguration or Initialize request.
+func updateGlobalOptions(options *settings.Options) {
+ if options.MaxFileCacheBytes > 0 {
+ filecache.SetBudget(options.MaxFileCacheBytes)
+ }
+ if options.MemoryLimit > 0 {
+ runtimedebug.SetMemoryLimit(options.MemoryLimit)
+ }
+}
+
func (s *server) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
ctx, done := event.Start(ctx, "server.Initialized")
defer done()
diff --git a/gopls/internal/server/workspace.go b/gopls/internal/server/workspace.go
index 66d682a..7e6a7dc 100644
--- a/gopls/internal/server/workspace.go
+++ b/gopls/internal/server/workspace.go
@@ -13,7 +13,6 @@

"golang.org/x/tools/gopls/internal/cache"
"golang.org/x/tools/gopls/internal/file"
- "golang.org/x/tools/gopls/internal/filecache"
"golang.org/x/tools/gopls/internal/golang/completion"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/settings"
@@ -144,9 +143,7 @@
// An options change may have affected the detected Go version.
s.checkViewGoVersions()

- if options.MaxFileCacheBytes > 0 {
- filecache.SetBudget(options.MaxFileCacheBytes)
- }
+ updateGlobalOptions(options)

return nil
}
diff --git a/gopls/internal/settings/settings.go b/gopls/internal/settings/settings.go
index b0a6477..19bf723 100644
--- a/gopls/internal/settings/settings.go
+++ b/gopls/internal/settings/settings.go
@@ -699,6 +699,25 @@
// as measured by du(1) may be significantly higher.
MaxFileCacheBytes int64 `status:"experimental"`

+ // MemoryLimit sets a soft memory limit (in bytes) for the gopls process, via
+ // runtime/debug.SetMemoryLimit. If non-positive (the default), no limit is set.
+ //
+ // On large workspaces, a single edit that invalidates many
+ // packages (for example a syntax error in a widely-imported
+ // package) can make the heap briefly grow well above the
+ // steady-state working set before the garbage collector
+ // catches up, spiking memory and, on memory-constrained
+ // machines, causing swapping. A soft limit makes the GC work
+ // harder to stay near the limit, trading some CPU for a lower
+ // memory peak.
+ //
+ // The limit is soft and may be exceeded. Set it comfortably above the
+ // steady-state working set, as too low a value causes excessive GC.
+ //
+ // Unlike the GOMEMLIMIT environment variable, this setting is
+ // strictly numeric; SI suffixes are not permitted.
+ MemoryLimit int64 `status:"experimental"`
+
// VerboseOutput enables additional debug logging.
VerboseOutput bool `status:"debug"`
}
@@ -1336,6 +1355,9 @@
case "maxFileCacheBytes":
return setInt64(&o.MaxFileCacheBytes, value)

+ case "memoryLimit":
+ return setInt64(&o.MemoryLimit, value)
+
case "verboseOutput":
return setBool(&o.VerboseOutput, value)

Change information

Files:
  • A gopls/doc/release/v0.24.0.md
  • M gopls/doc/settings.md
  • M gopls/internal/doc/api.json
  • M gopls/internal/protocol/tsjson.go
  • M gopls/internal/server/general.go
  • M gopls/internal/server/workspace.go
  • M gopls/internal/settings/settings.go
Change size: M
Delta: 7 files changed, 106 insertions(+), 11 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Idaf6390787a8895336ec27071e67014f4a17982a
Gerrit-Change-Number: 794882
Gerrit-PatchSet: 1
Gerrit-Owner: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Alan Donovan (Gerrit)

unread,
Jun 26, 2026, 5:16:36 PM (14 hours ago) Jun 26
to goph...@pubsubhelper.golang.org, Hongxiang Jiang, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Hongxiang Jiang

Alan Donovan voted Auto-Submit+1

Auto-Submit+1
Open in Gerrit

Related details

Attention is currently required from:
  • Hongxiang Jiang
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: Idaf6390787a8895336ec27071e67014f4a17982a
Gerrit-Change-Number: 794882
Gerrit-PatchSet: 1
Gerrit-Owner: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: Alan Donovan <adon...@google.com>
Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
Gerrit-Comment-Date: Fri, 26 Jun 2026 21:16:31 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages