---
title: Bun v1.0.20
description: "Reduced memory usage for fs.readlink, fs.writeFile, fs.stat and HTMLRewriter. Fixes a regression where setTimeout caused high CPU usage on Linux. Bun.spawn shows how much CPU & memory the process used. HTMLRewriter.transform now supports strings and ArrayBuffer. fs.writeFile() and fs.readFile() now support hex & base64 encodings"
date: "2023-12-24T14:05:40.001Z"
author: jarred
draft: false
---

Bun v1.0.20 reduces memory usage in `fs.readlink`, `fs.readFile`, `fs.writeFile`, `fs.stat` and `HTMLRewriter`. Fixes a regression where setTimeout caused high CPU usage on Linux. `HTMLRewriter.transform` now supports strings and `ArrayBuffer`. `fs.writeFile()` and `fs.readFile()` now support `hex` & `base64` encodings. `Bun.spawn` shows how much CPU & memory the process used.

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager &mdash; all in one. In case you missed it, here are some of the recent changes to Bun:

- [`v1.0.15`](/blog/bun-v1.0.15) - Fixes 23 bugs (addressing 117 👍 reactions), `tsc` starts 2x faster. Stable `WebSocket` client, syntax-highlighted errors, cleaner stack traces, add custom test matchers with `expect.extend()` + additional expect matchers.
- [`v1.0.16`](/blog/bun-v1.0.16) - Fixes 49 bugs (addressing 38 👍 reactions). Concurrent IO for Bun.file & Bun.write gets 3x faster and now supports Google Cloud Run & Vercel, Bun.write auto-creates the parent directory if it doesn't exist, `expect.extend` inside of preload works, `napi_create_object` gets 2.5x faster, bugfix for module resolution impacting Astro v4 and p-limit, console.log bugfixes
- [`v1.0.17`](/blog/bun-v1.0.17) - Fixes 15 bugs (addressing 152 👍 reactions). bun install postinstall scripts run for top 500 packages, `bunx supabase` starts 30x faster than `npx supabase`, `bunx esbuild` starts 50x faster than `npx esbuild` and bugfixes to bun install
- [`v1.0.18`](/blog/bun-v1.0.18) - Fixes 27 bugs (addressing 28 👍 reactions). A hang impacting create-vite & create-next & stdin has been fixed. Lifecycle scripts reporting "node" or "node-gyp" not found has been fixed. expect().rejects works like Jest now, and more bug fixes
- [`v1.0.19`](/blog/bun-v1.0.19) - Fixes 26 bugs (addressing 92 👍 reactions). Use @types/bun instead of bun-types. Fixes --frozen-lockfile bug. bcrypt & argon2 packages now work. setTimeout & setInterval get 4x higher throughput. module mocks in bun:test resolve specifiers. Optimized spawnSync() for large stdio on Linux. Bun.peek() gets 90x faster, expect(map1).toEqual(map2) gets 100x faster. Bugfixes to NAPI, bun install, and Node.js compatibility improvements

To install Bun:

{% codetabs %}

```sh#curl
$ curl -fsSL https://bun.sh/install | bash
```

```sh#npm
$ npm install -g bun
```

```sh#brew
$ brew tap oven-sh/bun
$ brew install bun
```

```sh#docker
$ docker pull oven/bun
$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun
```

{% /codetabs %}

To upgrade Bun:

```sh
$ bun upgrade
```

## Reduced memory usage in node:fs

This release makes infrastructure changes that reduce memory usage for async work involving strings throughout Bun.

We've also improved internal reporting of memory allocations in debug builds, which helps us identify memory leaks.

### `fs.readlink` uses up to 2x less memory

{% details summary="After reading a symlink 1,000,000 times in parallel" %}

```js
import { readlink } from "fs/promises";
const concurrency = 100;

let remaining = 1_000_000;
const lastargv = process.argv.at(-1);

while (remaining > 0) {
  const array = new Array(concurrency);
  for (let i = 0; i < array.length; i++) {
    array[i] = readlink(lastargv);
  }

  await Promise.all(array);
  remaining -= concurrency;
}

console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB");
```

{% /details %}

| Method        | JavaScript runtime | Platform    | RSS (MB) |
| ------------- | ------------------ | ----------- | -------- |
| `fs.readlink` | Bun v1.0.20        | macOS arm64 | 45 MB    |
| `fs.readlink` | Node.js v21.4.0    | macOS arm64 | 70 MB    |
| `fs.readlink` | Bun v1.0.19        | macOS arm64 | 107 MB   |

### `fs.stat` uses up to 2x less memory

{% details summary="After stat'ing a file 1,000,000 times in parallel" %}

```js
import { realpathSync } from "fs";
import { stat, writeFile } from "fs/promises";
import { resolve } from "path";
const concurrency = 100;

let remaining = 1_000_000;
const lastargv = process.argv.at(-1);

while (remaining > 0) {
  const array = new Array(concurrency);
  for (let i = 0; i < array.length; i++) {
    array[i] = stat(lastargv);
  }

  await Promise.all(array);
  remaining -= concurrency;
}

console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB");
```

{% /details %}

| Method    | JavaScript runtime   | Platform    | RSS (MB) |
| --------- | -------------------- | ----------- | -------- |
| `fs.stat` | Bun v1.0.20 `--smol` | macOS arm64 | 45 MB    |
| `fs.stat` | Bun v1.0.20          | macOS arm64 | 71 MB    |
| `fs.stat` | Node.js v21.4.0      | macOS arm64 | 70 MB    |
| `fs.stat` | Bun v1.0.19          | macOS arm64 | 107 MB   |

### `fs.writeFile` memory leak fixed

{% details summary="Writing a 16 MB non-ascii string to disk 100 times" %}

```js
import { Buffer } from "node:buffer";
import { writeFile } from "node:fs/promises";

const medFile =
  Buffer.alloc(1024 * 1024 * 16)
    .fill("a")
    .toString() + "😊";

for (let i = 0; i < 100; i++)
  await writeFile(
    "/tmp/bun.bench-out.medium.txt" +
      ((Math.random() * 65432) | 0).toString(16),
    medFile,
  );

console.log("RSS:", (process.memoryUsage().rss / 1024 / 1024) | 0, "MB");
```

{% /details %}

| Method         | JavaScript runtime | Platform    | RSS (MB) |
| -------------- | ------------------ | ----------- | -------- |
| `fs.writeFile` | Bun v1.0.20        | macOS arm64 | 91 MB    |
| `fs.writeFile` | Node.js v21.4.0    | macOS arm64 | 130 MB   |
| `fs.writeFile` | Bun v1.0.19        | macOS arm64 | 1,696 MB |

## `HTMLRewriter.transform` now supports strings and ArrayBuffer

`HTMLRewriter` lets you transform HTML on the fly. It's useful for things like:

- Rewriting URLs to point to a CDN
- Extracting metadata from HTML
- Getting all the images on a page

Bun's implementation of `HTMLRewriter` now supports passing strings and ArrayBuffers to `HTMLRewriter.transform`. Previously, you had to pass a `Response` object.

Passing a string to HTMLRewriter.transform will parse the string as HTML and return a string.

```js
const html = new HTMLRewriter()
  .on("img", {
    element(element) {
      element.setAttribute("src", "https://example.com/image.png");
    },
  })
  .on("title", {
    element(element) {
      element.setInnerContent("Hello world!");
    },
  })
  .transform(
    "<html><body><img src='image.png'><title>My page</title></body></html>",
  );

console.log(html);
// <html><body><img src="https://example.com/image.png"><title>Hello world!</title></body></html>
```

### Memory leak in HTMLRewriter fixed

A memory leak in HTMLRewriter has been fixed. This was caused by not calling the destructor at the appropriate time.

## fs.readFile() and fs.writeFile() now support hex & base64 encodings

`fs.readFile` and `fs.writeFile` now support `hex` and `base64` encodings. This is useful for reading and writing binary files.

```js
import { readFile, writeFile } from "fs/promises";

const buffer = await readFile("image.png", { encoding: "hex" });
await writeFile("image-copy.png", buffer, { encoding: "hex" });
```

## Fixed: regression where setTimeout caused high CPU usage on Linux

There was a regression in Bun v1.0.19 where `setTimeout` caused high CPU usage on Linux. This was fixed, thanks to [@cirospaciari](https://github.com/cirospaciari).

## Bun.spawn now reports `resourceUsage`

`Bun.spawn` & `Bun.spawnSync` gets a `resourceUsage` method, which reports CPU & memory usage for the process.

```js
import { spawnSync } from "bun";

// for spawnSync, it is a property on the return value
const { resourceUsage } = spawnSync([
  "bun",
  "-e",
  "console.log('Hello world!')",
]);

console.log(resourceUsage);

// in Bun.spawn, it is a function that you call
```

This prints:

```js
ResourceUsage {
  contextSwitches: {
    voluntary: 0,
    involuntary: 120,
  },
  cpuTime: {
    user: 5578n,
    system: 4488n,
    total: 10066n,
  },
  maxRSS: 22020096,
  messages: {
    sent: 0,
    received: 0,
  },
  ops: {
    in: 0,
    out: 0,
  },
  shmSize: 0,
  signalCount: 0,
  swapCount: 0,
}
```

Thanks to [@cirospaciari](https://github.com/cirospaciari) for this contribution!

We used this to write tests to prevent the regression where `setTimeout` caused high CPU usage on Linux from happening again.

### Infrastructure improvements

In debug builds of Bun, we've added support for the 'malloc zone' feature of macOS to track memory allocations in native code more accurately. This helps us identify memory leaks.

This prints output like the below:

```sh
DefaultMallocZone:
  blocks_in_use:   278
  size_in_use:     2318192
  max_size_in_use: 2696288
  size_allocated:  19922944

WebKit Malloc:
  blocks_in_use:   0
  size_in_use:     0
  max_size_in_use: 0
  size_allocated:  0

Bun__Response:
  blocks_in_use:   2
  size_in_use:     240
  max_size_in_use: 16976
  size_allocated:  1048576

Bun__BufferOutputSink:
  blocks_in_use:   1
  size_in_use:     32
  max_size_in_use: 16832
  size_allocated:  1048576
```

We've also changed how we do cross-thread cloning of JavaScript strings. Previously, we sometimes cloned a string twice and then never used the first clone. This was extremely wasteful. Now, we only clone a string once and only if we definitely need to.

## `bun init` now uses `@types/bun`

`bun init` now uses `@types/bun` instead of `bun-types`, and we've enabled some of the new options like `verbatimModuleSyntax`.

Thanks to [@ArnaudBarre](https://github.com/ArnaudBarre) for this contribution!

## Thanks to five contributors!

- [@ArnaudBarre](https://github.com/ArnaudBarre)
- [@cirospaciari](https://github.com/cirospaciari)
- [@Jarred-Sumner](https://github.com/Jarred-Sumner)
- [@risu729](https://github.com/risu729)
- [@vitalspace](https://github.com/vitalspace)

[**Full Changelog**](https://github.com/oven-sh/bun/compare/bun-v1.0.19...bun-v1.0.20)
