Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit dfc81e3

Browse files
author
Stephen Gutekanst
committed
Add -freeosmemory=true flag & invoke FreeOSMemory periodically (lower memory consumption)
To try this change out: 1. `git checkout sg/mem` to the exact commit. 2. `go install github.com/sourcegraph/go-langserver` 3. Open some Go code and hover over a symbol, then make an edit, then hover, repeat... (each edit and hover triggers type-checking again). 4. You can also set `"go.languageServerFlags": ["-freeosmemory=false"]` in VS Code settings to try without this change (to compare memory usage). With `code github.com/docker/docker/cmd/dockerd/docker.go`, and making 10 edits/hovers, the change is: | Real Before | Real After | Real Change | Go Before | Go After | Go Change | |-------------|------------|-------------|-----------|----------|-----------| | 7.61GB | 4.12GB | -45.86% | 3.92GB | 3.33GB | -15.05% | Where `Real` means real memory reported by OS X Activity Monitor, and `Go` means memory reported by Go as being in use. TL;DR: 46% less memory consumption for users running with the vscode-go extension.
1 parent dad0588 commit dfc81e3

File tree

1 file changed

+45
-0
lines changed

1 file changed

+45
-0
lines changed

main.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"net"
1010
"net/http"
1111
"os"
12+
"runtime/debug"
13+
"time"
1214

1315
"github.com/sourcegraph/go-langserver/langserver"
1416
"github.com/sourcegraph/jsonrpc2"
@@ -23,6 +25,7 @@ var (
2325
logfile = flag.String("logfile", "", "also log to this file (in addition to stderr)")
2426
printVersion = flag.Bool("version", false, "print version and exit")
2527
pprof = flag.String("pprof", ":6060", "start a pprof http server (https://golang.org/pkg/net/http/pprof/)")
28+
freeosmemory = flag.Bool("freeosmemory", true, "aggressively free memory back to the OS")
2629
)
2730

2831
// version is the version field we report back. If you are releasing a new version:
@@ -43,6 +46,10 @@ func main() {
4346
}()
4447
}
4548

49+
if *freeosmemory {
50+
go freeOSMemory()
51+
}
52+
4653
if err := run(); err != nil {
4754
fmt.Fprintln(os.Stderr, err)
4855
os.Exit(1)
@@ -117,3 +124,41 @@ func (stdrwc) Close() error {
117124
}
118125
return os.Stdout.Close()
119126
}
127+
128+
// freeOSMemory should be called in a goroutine, it invokes
129+
// runtime/debug.FreeOSMemory() more aggressively than the runtime default of
130+
// 5 minutes after GC.
131+
//
132+
// There is a long-standing known issue with Go in which memory is not returned
133+
// to the OS aggressively enough[1], which coincidently harms our application
134+
// quite a lot because we perform so many short-burst heap allocations during
135+
// the type-checking phase.
136+
//
137+
// This function should only be invoked in editor mode, not in sourcegraph.com
138+
// mode, because users running the language server as part of their editor
139+
// generally expect much lower memory usage. In contrast, on sourcegraph.com we
140+
// can give our servers plenty of RAM and allow Go to consume as much as it
141+
// wants. Go does reuse the memory not free'd to the OS, and as such enabling
142+
// this does _technically_ make our application perform less optimally -- but
143+
// in practice this has no observable effect in editor mode.
144+
//
145+
// The end effect of performing this is that repeating "hover over code" -> "make an edit"
146+
// 10 times inside a large package like github.com/docker/docker/cmd/dockerd:
147+
//
148+
//
149+
// | Real Before | Real After | Real Change | Go Before | Go After | Go Change |
150+
// |-------------|------------|-------------|-----------|----------|-----------|
151+
// | 7.61GB | 4.12GB | -45.86% | 3.92GB | 3.33GB | -15.05% |
152+
//
153+
// Where `Real` means real memory reported by OS X Activity Monitor, and `Go`
154+
// means memory reported by Go as being in use.
155+
//
156+
// TL;DR: 46% less memory consumption for users running with the vscode-go extension.
157+
//
158+
// [1] https://github.com/golang/go/issues/14735#issuecomment-194470114
159+
func freeOSMemory() {
160+
for {
161+
time.Sleep(1 * time.Second)
162+
debug.FreeOSMemory()
163+
}
164+
}

0 commit comments

Comments
 (0)