Buffer.allocUnsafe()

Allocates a Buffer without zeroing it, trading initialised memory for allocation speed.

Signature
Buffer.allocUnsafe(size[, alignment])
  • Added in Node.js v5.10.0

Parameters

ParameterTypeDescription
$sizeintegerThe desired length of the new Buffer.
$alignmentoptionalintegerIf given, the memory backing the new Buffer will start at an address that is a multiple of alignment. Must be a power of two no larger than 2 ** 30.

Return value

BufferA new Buffer of size bytes whose contents are undefined until written.

Errors

Throws ERR_OUT_OF_RANGE if size is negative or greater than buffer.constants.MAX_LENGTH, and ERR_INVALID_ARG_TYPE if size is not a number.

Changelog

  • PHP v26.8.0Added the alignment argument; on earlier releases a second argument is ignored.
  • PHP v20.0.0Invalid arguments now throw ERR_INVALID_ARG_TYPE or ERR_OUT_OF_RANGE instead of ERR_INVALID_ARG_VALUE.
  • PHP v7.0.0Passing a negative size now throws instead of returning an empty buffer.

Buffer.allocUnsafe() skips the zeroing pass that Buffer.alloc() performs. The returned buffer contains whatever those bytes held previously — often the remains of an earlier buffer from the same process, which may be a password, a session token or another user's request body.

The contract you are accepting

You must write every byte you will later read or send. If you allocate 64 bytes, write 40 and hand the whole buffer onward, the last 24 bytes are leaked memory. The safe pattern is to allocate, fill immediately, and pass a subarray of only the region you actually wrote.

Why it is faster

Allocations smaller than Buffer.poolSize >> 1 (2 KiB by default) are carved out of a shared pre-allocated pool, so there is no per-call allocation at all. Larger requests bypass the pool. The win is real only in hot paths that allocate constantly; for a handful of buffers, the difference is unmeasurable and the risk is not worth it.

Buffer.allocUnsafeSlow() does the same thing but never uses the pool, which matters when you intend to retain a small buffer for a long time and do not want it pinning an 8 KiB chunk.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Allocate a read target in a hot loop where fs.read overwrites the whole block.
  • Build a fixed-size binary frame whose every field is written immediately.
  • Avoid the zero-fill cost when generating large buffers filled from a source.
  • Use allocUnsafeSlow for a small buffer you will retain, to keep it off the shared pool.

Frequently asked questions

Why does Buffer.allocUnsafe contain random garbage?

Because it returns memory without clearing it, so you see whatever the previous owner of those bytes left behind. That content is not random in the cryptographic sense; it can be recognisable data from elsewhere in your process.

Is Buffer.allocUnsafe actually faster than Buffer.alloc?

Yes, because it skips the zero fill and, for sizes below Buffer.poolSize >> 1, carves the buffer out of an existing pool instead of allocating. The gap only shows up in code that allocates in a tight loop.

When should I use allocUnsafeSlow instead of allocUnsafe?

Use allocUnsafeSlow for a small buffer you will keep for a long time, because allocUnsafe would take it from the shared 8 KiB pool and keep that entire slab alive. For short-lived buffers the pooled version is the better choice.

More examples

The leak this causes

This is the failure mode that made the old new Buffer(size) constructor a security problem. The fix is not to inspect the buffer but to bound it: keep the byte count buf.write returned and pass only that region onward.

Example · javascript
// Something earlier in the process used this memory.
Buffer.from('password=hunter2&session=abc123').fill(0x41); // pretend it was freed

const buf = Buffer.allocUnsafe(32);
buf.write('OK');            // only 2 bytes written

// Sending the whole buffer sends 30 bytes you never set.
socket.write(buf);          // WRONG

// Send only what you wrote.
const written = buf.write('OK');
socket.write(buf.subarray(0, written));  // right

A read loop where it is justified

One buffer is allocated once and reused, and every iteration only touches the prefix that readSync actually filled. The zero fill would be pure waste here because the kernel overwrites the region anyway.

Example · javascript
const fs = require('node:fs');

const fd = fs.openSync('data.bin', 'r');
const chunk = Buffer.allocUnsafe(64 * 1024);
let total = 0;

for (;;) {
  // read() overwrites bytes 0..bytesRead, and we never look past that.
  const bytesRead = fs.readSync(fd, chunk, 0, chunk.length, null);
  if (bytesRead === 0) break;
  total += bytesRead;
  process.stdout.write(chunk.subarray(0, bytesRead));
}

fs.closeSync(fd);
console.log('bytes copied:', total);

Pooling, and when to use allocUnsafeSlow

Buffers under half of Buffer.poolSize share one backing ArrayBuffer, which is why small.buffer.byteLength reports 8192 rather than 16. If you cache such a buffer for the life of the process, allocUnsafeSlow gives it a private allocation so the shared slab can be collected.

Example · javascript
console.log('poolSize:', Buffer.poolSize);            // 8192

const small = Buffer.allocUnsafe(16);
console.log('pooled  :', small.buffer.byteLength === Buffer.poolSize); // true

const big = Buffer.allocUnsafe(Buffer.poolSize);
console.log('unpooled:', big.buffer.byteLength === big.length);       // true

// A retained small buffer keeps the whole 8 KiB slab alive.
const retained = Buffer.allocUnsafeSlow(16);
console.log('own slab:', retained.buffer.byteLength);                 // 16

Discussion

  • Be the first to comment on this lesson.