aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-12-01 02:34:15 -0800
committerGravatar GitHub <noreply@github.com> 2022-12-01 02:34:15 -0800
commitbddf523ac9d201a1d15aa8b938b516aa1a6949aa (patch)
treef073b450e989954d7389ea4654bced3e96727d92
parent1506a25198ce1c09d102c6a7a88ed24cc2a8be8a (diff)
downloadbun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.tar.gz
bun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.tar.zst
bun-bddf523ac9d201a1d15aa8b938b516aa1a6949aa.zip
Reduce memory usage in Bun.serve() by up to 3x (#1569)
* Update WebKit * Use 5x less memory in Bun.serve() * Update Dockerfile.devcontainer * Update async-overhead.mjs Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--.github/workflows/bun-linux-build.yml6
-rw-r--r--.github/workflows/bun-mac-aarch64.yml16
-rw-r--r--.github/workflows/bun-mac-x64-baseline.yml16
-rw-r--r--.github/workflows/bun-mac-x64.yml16
-rw-r--r--Dockerfile.devcontainer2
-rw-r--r--bench/snippets/async-overhead.mjs15
m---------src/bun.js/WebKit0
-rw-r--r--src/bun.js/api/bun/socket.zig3
-rw-r--r--src/bun.js/api/server.zig11
-rw-r--r--src/bun.js/bindings/bindings.cpp17
-rw-r--r--src/bun.js/bindings/bindings.zig13
-rw-r--r--src/bun.js/bindings/headers-cpp.h2
-rw-r--r--src/bun.js/bindings/headers.h4
-rw-r--r--src/bun.js/bindings/headers.zig2
-rw-r--r--src/bun.js/event_loop.zig128
-rw-r--r--src/bun.js/javascript.zig2
16 files changed, 216 insertions, 37 deletions
diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml
index db40ed84d..9b6b8234f 100644
--- a/.github/workflows/bun-linux-build.yml
+++ b/.github/workflows/bun-linux-build.yml
@@ -39,21 +39,21 @@ jobs:
arch: x86_64
build_arch: amd64
runner: linux-amd64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-amd64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-amd64-lto"
- cpu: westmere
tag: linux-x64-baseline
arch: x86_64
build_arch: amd64
runner: linux-amd64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-amd64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-amd64-lto"
- cpu: native
tag: linux-aarch64
arch: aarch64
build_arch: arm64
runner: linux-arm64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-arm64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-arm64-lto.tar.gz"
webkit_basename: "bun-webkit-linux-arm64-lto"
steps:
diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml
index 7956fba37..f6b52e6ed 100644
--- a/.github/workflows/bun-mac-aarch64.yml
+++ b/.github/workflows/bun-mac-aarch64.yml
@@ -91,7 +91,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
# - cpu: haswell
@@ -100,7 +100,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
# - cpu: westmere
@@ -109,7 +109,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
# - cpu: haswell
@@ -118,7 +118,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
- cpu: native
@@ -126,7 +126,7 @@ jobs:
tag: bun-darwin-aarch64
obj: bun-obj-darwin-aarch64
artifact: bun-obj-darwin-aarch64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz"
runner: macos-arm64
dependencies: true
compile_obj: true
@@ -230,7 +230,7 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: haswell
# arch: x86_64
# tag: bun-darwin-x64
@@ -238,14 +238,14 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
- cpu: native
arch: aarch64
tag: bun-darwin-aarch64
obj: bun-obj-darwin-aarch64
package: bun-darwin-aarch64
artifact: bun-obj-darwin-aarch64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz"
runner: macos-arm64
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/bun-mac-x64-baseline.yml b/.github/workflows/bun-mac-x64-baseline.yml
index 0f5f5e8a4..4eb4067f9 100644
--- a/.github/workflows/bun-mac-x64-baseline.yml
+++ b/.github/workflows/bun-mac-x64-baseline.yml
@@ -91,7 +91,7 @@ jobs:
obj: bun-obj-darwin-x64-baseline
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: true
compile_obj: false
# - cpu: haswell
@@ -100,7 +100,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
- cpu: westmere
@@ -109,7 +109,7 @@ jobs:
obj: bun-obj-darwin-x64-baseline
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: false
compile_obj: true
# - cpu: haswell
@@ -118,7 +118,7 @@ jobs:
# obj: bun-obj-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
# - cpu: native
@@ -126,7 +126,7 @@ jobs:
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# runner: macos-arm64
# dependencies: true
# compile_obj: true
@@ -231,7 +231,7 @@ jobs:
package: bun-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64-baseline
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: haswell
# arch: x86_64
# tag: bun-darwin-x64
@@ -239,14 +239,14 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: native
# arch: aarch64
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# package: bun-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# runner: macos-arm64
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/bun-mac-x64.yml b/.github/workflows/bun-mac-x64.yml
index 20db3ef5e..83c8393de 100644
--- a/.github/workflows/bun-mac-x64.yml
+++ b/.github/workflows/bun-mac-x64.yml
@@ -90,7 +90,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: true
# compile_obj: false
- cpu: haswell
@@ -99,7 +99,7 @@ jobs:
obj: bun-obj-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: true
compile_obj: false
# - cpu: westmere
@@ -108,7 +108,7 @@ jobs:
# obj: bun-obj-darwin-x64-baseline
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# dependencies: false
# compile_obj: true
- cpu: haswell
@@ -117,7 +117,7 @@ jobs:
obj: bun-obj-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
dependencies: false
compile_obj: true
# - cpu: native
@@ -125,7 +125,7 @@ jobs:
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz"
# runner: macos-arm64
# dependencies: true
# compile_obj: true
@@ -232,7 +232,7 @@ jobs:
# package: bun-darwin-x64
# runner: macos-11
# artifact: bun-obj-darwin-x64-baseline
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
- cpu: haswell
arch: x86_64
tag: bun-darwin-x64
@@ -240,14 +240,14 @@ jobs:
package: bun-darwin-x64
runner: macos-11
artifact: bun-obj-darwin-x64
- webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-amd64-lto.tar.gz"
+ webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-amd64-lto.tar.gz"
# - cpu: native
# arch: aarch64
# tag: bun-darwin-aarch64
# obj: bun-obj-darwin-aarch64
# package: bun-darwin-aarch64
# artifact: bun-obj-darwin-aarch64
- # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-macos-arm64-lto.tar.gz"
+ # webkit_url: "https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-macos-arm64-lto.tar.gz"
# runner: macos-arm64
steps:
- uses: actions/checkout@v3
diff --git a/Dockerfile.devcontainer b/Dockerfile.devcontainer
index 45348a4d6..7d32e9162 100644
--- a/Dockerfile.devcontainer
+++ b/Dockerfile.devcontainer
@@ -90,7 +90,7 @@ RUN cd $GITHUB_WORKSPACE && \
rm zig-linux-$BUILDARCH.zip;
RUN cd $GITHUB_WORKSPACE && \
- curl -o bun-webkit-linux-$BUILDARCH.tar.gz -L https://github.com/oven-sh/WebKit/releases/download/nov24/bun-webkit-linux-$BUILDARCH.tar.gz && \
+ curl -o bun-webkit-linux-$BUILDARCH.tar.gz -L https://github.com/oven-sh/WebKit/releases/download/dec1/bun-webkit-linux-$BUILDARCH.tar.gz && \
tar -xzf bun-webkit-linux-$BUILDARCH.tar.gz && \
rm bun-webkit-linux-$BUILDARCH.tar.gz && \
cat $WEBKIT_OUT_DIR/include/cmakeconfig.h > /dev/null
diff --git a/bench/snippets/async-overhead.mjs b/bench/snippets/async-overhead.mjs
index f05b348e6..bec278b56 100644
--- a/bench/snippets/async-overhead.mjs
+++ b/bench/snippets/async-overhead.mjs
@@ -5,6 +5,19 @@ bench("async function(){}", async function () {});
bench("await 1", async function () {
return await 1;
});
+
+function callnextTick(resolve) {
+ process.nextTick(resolve);
+}
+
+function awaitNextTick() {
+ return new Promise(callnextTick);
+}
+
+bench("promise.nextTick", async function () {
+ return awaitNextTick();
+});
+
bench("await new Promise(resolve => resolve())", async function () {
await new Promise((resolve) => resolve());
});
@@ -12,7 +25,7 @@ bench(
"Promise.all(Array.from({length: 100}, () => new Promise((resolve) => resolve())))",
async function () {
return Promise.all(Array.from({ length: 100 }, () => Promise.resolve(1)));
- }
+ },
);
await run();
diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit
-Subproject 6e5bbee4a3f5fe81a095067e5d0f8d8f891f79d
+Subproject 7e5db543dbeb0f5510c18b83b94c79ca391514f
diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig
index 78829f428..fa4411cc0 100644
--- a/src/bun.js/api/bun/socket.zig
+++ b/src/bun.js/api/bun/socket.zig
@@ -409,6 +409,7 @@ pub const Listener = struct {
ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode);
}
+ globalObject.bunVM().eventLoop().ensureWaker();
socket.socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts);
if (ssl) |ssl_config| {
@@ -661,6 +662,8 @@ pub const Listener = struct {
ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode);
}
+ globalObject.bunVM().eventLoop().ensureWaker();
+
var socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts).?;
var connection: Listener.UnixOrHost = if (port) |port_| .{
.host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port_ },
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index fb991481f..e2892f88f 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -4169,6 +4169,8 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
}
pub fn onRequestComplete(this: *ThisServer) void {
+ this.vm.eventLoop().processGCTimer();
+
this.pending_requests -= 1;
this.deinitIfWeCan();
}
@@ -4567,7 +4569,14 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type {
this.config.hostname;
this.ref();
- this.vm.autoGarbageCollect();
+
+ // Starting up an HTTP server is a good time to GC
+ if (this.vm.aggressive_garbage_collection == .aggressive) {
+ this.vm.autoGarbageCollect();
+ } else {
+ this.vm.eventLoop().performGC();
+ }
+
this.app.listenWithConfig(*ThisServer, this, onListen, .{
.port = this.config.port,
.host = host,
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp
index e2e42f38c..3af4b8421 100644
--- a/src/bun.js/bindings/bindings.cpp
+++ b/src/bun.js/bindings/bindings.cpp
@@ -3432,3 +3432,20 @@ restart:
return;
}
}
+
+extern "C" size_t JSC__VM__blockBytesAllocated(JSC__VM* vm)
+{
+#if ENABLE(RESOURCE_USAGE)
+ return vm->heap.blockBytesAllocated();
+#else
+ return 0;
+#endif
+}
+extern "C" size_t JSC__VM__externalMemorySize(JSC__VM* vm)
+{
+#if ENABLE(RESOURCE_USAGE)
+ return vm->heap.externalMemorySize();
+#else
+ return 0;
+#endif
+} \ No newline at end of file
diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig
index 15e15b032..d17cc5382 100644
--- a/src/bun.js/bindings/bindings.zig
+++ b/src/bun.js/bindings/bindings.zig
@@ -3385,7 +3385,18 @@ pub const VM = extern struct {
vm,
});
}
- pub const Extern = [_][]const u8{ "collectAsync", "heapSize", "releaseWeakRefs", "throwError", "doWork", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" };
+
+ pub fn externalMemorySize(vm: *VM) usize {
+ return cppFn("externalMemorySize", .{vm});
+ }
+
+ /// `RESOURCE_USAGE` build option in JavaScriptCore is required for this function
+ /// This is faster than checking the heap size
+ pub fn blockBytesAllocated(vm: *VM) usize {
+ return cppFn("blockBytesAllocated", .{vm});
+ }
+
+ pub const Extern = [_][]const u8{ "collectAsync", "externalMemorySize", "blockBytesAllocated", "heapSize", "releaseWeakRefs", "throwError", "doWork", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" };
};
pub const ThrowScope = extern struct {
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index e35afe0f0..34fdaba99 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1669793662
+//-- AUTOGENERATED FILE -- 1669880224
// clang-format off
#pragma once
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index 399fc54a6..75701502d 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1669793662
+//-- AUTOGENERATED FILE -- 1669880224
#pragma once
#include <stddef.h>
@@ -337,6 +337,7 @@ CPP_DECL JSC__JSValue JSC__Exception__value(JSC__Exception* arg0);
#pragma mark - JSC::VM
+CPP_DECL size_t JSC__VM__blockBytesAllocated(JSC__VM* arg0);
CPP_DECL void JSC__VM__clearExecutionTimeLimit(JSC__VM* arg0);
CPP_DECL void JSC__VM__collectAsync(JSC__VM* arg0);
CPP_DECL JSC__VM* JSC__VM__create(unsigned char HeapType0);
@@ -346,6 +347,7 @@ CPP_DECL void JSC__VM__deleteAllCode(JSC__VM* arg0, JSC__JSGlobalObject* arg1);
CPP_DECL void JSC__VM__doWork(JSC__VM* arg0);
CPP_DECL void JSC__VM__drainMicrotasks(JSC__VM* arg0);
CPP_DECL bool JSC__VM__executionForbidden(JSC__VM* arg0);
+CPP_DECL size_t JSC__VM__externalMemorySize(JSC__VM* arg0);
CPP_DECL size_t JSC__VM__heapSize(JSC__VM* arg0);
CPP_DECL void JSC__VM__holdAPILock(JSC__VM* arg0, void* arg1, void (* ArgFn2)(void* arg0));
CPP_DECL bool JSC__VM__isEntered(JSC__VM* arg0);
diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig
index e99db7ef0..b0bfadae9 100644
--- a/src/bun.js/bindings/headers.zig
+++ b/src/bun.js/bindings/headers.zig
@@ -254,6 +254,7 @@ pub extern fn JSC__JSValue__toZigString(JSValue0: JSC__JSValue, arg1: [*c]ZigStr
pub extern fn JSC__Exception__create(arg0: ?*JSC__JSGlobalObject, arg1: [*c]JSC__JSObject, StackCaptureAction2: u8) [*c]JSC__Exception;
pub extern fn JSC__Exception__getStackTrace(arg0: [*c]JSC__Exception, arg1: [*c]ZigStackTrace) void;
pub extern fn JSC__Exception__value(arg0: [*c]JSC__Exception) JSC__JSValue;
+pub extern fn JSC__VM__blockBytesAllocated(arg0: [*c]JSC__VM) usize;
pub extern fn JSC__VM__clearExecutionTimeLimit(arg0: [*c]JSC__VM) void;
pub extern fn JSC__VM__collectAsync(arg0: [*c]JSC__VM) void;
pub extern fn JSC__VM__create(HeapType0: u8) [*c]JSC__VM;
@@ -263,6 +264,7 @@ pub extern fn JSC__VM__deleteAllCode(arg0: [*c]JSC__VM, arg1: ?*JSC__JSGlobalObj
pub extern fn JSC__VM__doWork(arg0: [*c]JSC__VM) void;
pub extern fn JSC__VM__drainMicrotasks(arg0: [*c]JSC__VM) void;
pub extern fn JSC__VM__executionForbidden(arg0: [*c]JSC__VM) bool;
+pub extern fn JSC__VM__externalMemorySize(arg0: [*c]JSC__VM) usize;
pub extern fn JSC__VM__heapSize(arg0: [*c]JSC__VM) usize;
pub extern fn JSC__VM__holdAPILock(arg0: [*c]JSC__VM, arg1: ?*anyopaque, ArgFn2: ?fn (?*anyopaque) callconv(.C) void) void;
pub extern fn JSC__VM__isEntered(arg0: [*c]JSC__VM) bool;
diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig
index f9f11c943..aac1e3dcd 100644
--- a/src/bun.js/event_loop.zig
+++ b/src/bun.js/event_loop.zig
@@ -220,6 +220,21 @@ pub const EventLoop = struct {
start_server_on_next_tick: bool = false,
defer_count: std.atomic.Atomic(usize) = std.atomic.Atomic(usize).init(0),
+ gc_timer: *uws.Timer = undefined,
+ gc_last_heap_size: usize = 0,
+ gc_last_heap_size_on_repeating_timer: usize = 0,
+ heap_size_didnt_change_for_repeating_timer_ticks_count: u8 = 0,
+ gc_timer_state: GCTimerState = GCTimerState.pending,
+ gc_repeating_timer: *uws.Timer = undefined,
+ gc_timer_interval: i32 = 0,
+ gc_repeating_timer_fast: bool = true,
+
+ pub const GCTimerState = enum {
+ pending,
+ scheduled,
+ run_on_next_tick,
+ };
+
pub const Queue = std.fifo.LinearFifo(Task, .Dynamic);
pub fn tickWithCount(this: *EventLoop) u32 {
@@ -333,14 +348,59 @@ pub const EventLoop = struct {
var loop = this.virtual_machine.uws_event_loop.?;
if (loop.num_polls > 0 or loop.active > 0) {
loop.tick();
+ this.processGCTimer();
// this.afterUSocketsTick();
}
}
+ pub fn scheduleGCTimer(this: *EventLoop) void {
+ this.gc_timer_state = .scheduled;
+ this.gc_timer.set(this, onGCTimer, 16, 0);
+ }
+
+ pub fn processGCTimer(this: *EventLoop) void {
+ var vm = this.virtual_machine.global.vm();
+ const this_heap_size = vm.blockBytesAllocated();
+ const prev = this.gc_last_heap_size;
+
+ switch (this.gc_timer_state) {
+ .run_on_next_tick => {
+ if (this_heap_size > prev) {
+ this.scheduleGCTimer();
+ this.updateGCRepeatTimer(.fast);
+ } else {
+ this.gc_timer_state = .pending;
+ }
+ vm.collectAsync();
+ this.gc_last_heap_size = this_heap_size;
+ },
+ .pending => {
+ if (this_heap_size > prev) {
+ this.updateGCRepeatTimer(.fast);
+
+ if (this_heap_size > prev * 2) {
+ this.performGC();
+ } else {
+ this.scheduleGCTimer();
+ }
+ }
+ },
+ .scheduled => {
+ if (this_heap_size > prev * 2) {
+ this.updateGCRepeatTimer(.fast);
+ this.performGC();
+ }
+ },
+ }
+ }
+
// TODO: fix this technical debt
pub fn tick(this: *EventLoop) void {
var ctx = this.virtual_machine;
this.tickConcurrent();
+
+ this.processGCTimer();
+
var global_vm = ctx.global.vm();
while (true) {
while (this.tickWithCount() > 0) {
@@ -373,10 +433,6 @@ pub const EventLoop = struct {
if (loop.active > 0 or (ctx.us_loop_reference_count > 0 and !ctx.is_us_loop_entered and (loop.num_polls > 0 or this.start_server_on_next_tick))) {
if (this.tickConcurrentWithCount() > 0) {
this.tick();
- } else {
- if ((@intCast(c_ulonglong, ctx.uws_event_loop.?.internal_loop_data.iteration_nr) % 1_000) == 1) {
- _ = ctx.global.vm().runGC(true);
- }
}
ctx.is_us_loop_entered = true;
@@ -427,11 +483,75 @@ pub const EventLoop = struct {
if (this.virtual_machine.uws_event_loop == null) {
var actual = uws.Loop.get().?;
this.virtual_machine.uws_event_loop = actual;
+ this.gc_timer = uws.Timer.create(actual, this);
+ this.gc_repeating_timer = uws.Timer.create(actual, this);
+
+ var gc_timer_interval: i32 = 1000;
+ if (this.virtual_machine.bundler.env.map.get("BUN_GC_TIMER_INTERVAL")) |timer| {
+ if (std.fmt.parseInt(i32, timer, 10)) |parsed| {
+ if (parsed > 0) {
+ gc_timer_interval = parsed;
+ }
+ } else |_| {}
+ }
+ this.gc_repeating_timer.set(this, onGCRepeatingTimer, gc_timer_interval, gc_timer_interval);
+ this.gc_timer_interval = gc_timer_interval;
// _ = actual.addPostHandler(*JSC.EventLoop, this, JSC.EventLoop.afterUSocketsTick);
// _ = actual.addPreHandler(*JSC.VM, this.virtual_machine.global.vm(), JSC.VM.drainMicrotasks);
}
}
+ pub fn onGCTimer(timer: *uws.Timer) callconv(.C) void {
+ var this = timer.as(*EventLoop);
+ this.gc_timer_state = .run_on_next_tick;
+ }
+
+ // We want to always run GC once in awhile
+ // But if you have a long-running instance of Bun, you don't want the
+ // program constantly using CPU doing GC for no reason
+ //
+ // So we have two settings for this GC timer:
+ //
+ // - Fast: GC runs every 1 second
+ // - Slow: GC runs every 30 seconds
+ //
+ // When the heap size is increasing, we always switch to fast mode
+ // When the heap size has been the same or less for 30 seconds, we switch to slow mode
+ pub fn updateGCRepeatTimer(this: *EventLoop, comptime setting: @Type(.EnumLiteral)) void {
+ if (setting == .fast and !this.gc_repeating_timer_fast) {
+ this.gc_repeating_timer_fast = true;
+ this.gc_repeating_timer.set(this, onGCRepeatingTimer, this.gc_timer_interval, this.gc_timer_interval);
+ this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0;
+ } else if (setting == .slow and this.gc_repeating_timer_fast) {
+ this.gc_repeating_timer_fast = false;
+ this.gc_repeating_timer.set(this, onGCRepeatingTimer, 30_000, 30_000);
+ this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0;
+ }
+ }
+
+ pub fn onGCRepeatingTimer(timer: *uws.Timer) callconv(.C) void {
+ var this = timer.as(*EventLoop);
+ const prev_heap_size = this.gc_last_heap_size_on_repeating_timer;
+ this.performGC();
+ this.gc_last_heap_size_on_repeating_timer = this.gc_last_heap_size;
+ if (prev_heap_size == this.gc_last_heap_size_on_repeating_timer) {
+ this.heap_size_didnt_change_for_repeating_timer_ticks_count +|= 1;
+ if (this.heap_size_didnt_change_for_repeating_timer_ticks_count >= 30) {
+ // make the timer interval longer
+ this.updateGCRepeatTimer(.slow);
+ }
+ } else {
+ this.heap_size_didnt_change_for_repeating_timer_ticks_count = 0;
+ this.updateGCRepeatTimer(.fast);
+ }
+ }
+
+ /// Asynchronously run the garbage collector and track how much memory is now allocated
+ pub fn performGC(this: *EventLoop) void {
+ this.global.vm().collectAsync();
+ this.gc_last_heap_size = this.global.vm().blockBytesAllocated();
+ }
+
pub fn enqueueTaskConcurrent(this: *EventLoop, task: *ConcurrentTask) void {
JSC.markBinding(@src());
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index 6f966df2b..54fec7a96 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -1333,6 +1333,7 @@ pub const VirtualMachine = struct {
// pending_internal_promise can change if hot module reloading is enabled
if (this.bun_watcher != null) {
+ this.eventLoop().performGC();
switch (this.pending_internal_promise.status(this.global.vm())) {
JSC.JSPromise.Status.Pending => {
while (this.pending_internal_promise.status(this.global.vm()) == .Pending) {
@@ -1346,6 +1347,7 @@ pub const VirtualMachine = struct {
else => {},
}
} else {
+ this.eventLoop().performGC();
this.waitForPromise(promise);
}