Working with memory in JS — Array Buffer

Vladislav Lipatov
4 min readJul 29, 2021

--

How many browser tabs will occupy your RAM? Why does the browser get so much RAM? Is it possible to reduce the amount of memory consumption? In this article, I will try to answer all these questions.

The story

If you are familiar with JS you should know that there are no free or malloc -like functions in JS. The memory is allocated dynamically and then is released by the Garbage Collector when necessary. Some developers can write memory-unfriendly algorithms and that’s why some sites take so much memory. Is there a way to make it better? Yes! Follow me, I will show you the way.

Understand the basics

First of all, let’s know how much some memory primitive types take. Here I am not considering null , undefined , symbol types.

A number variable takes 8 bytes, following the international IEEE 754 standard.

A string variable depends on the string length: one character takes 2 bytes.

A boolean variable… Well, this is the tricky thing. The point is that the size really depends on the underlying environment (By platform I mean JS engine which implements memory management). But basically, it can be 1 byte or 4 bytes.

What about objects?

It’s difficult to compute object size because it depends on the keys and values that the object contains. Often objects can contain nested objects, so the task gets more complicated. Moreover, we need to consider the fact that each Javascript engine implements memory management in a different way. Because of that, the object size can be different depending on the browser. For instance, Google Chrome Heap Profiler has two different columns for an object size: shallow size and retained size.

Shallow size is the size of memory that is held by the object itself.

Retained size is the size of memory that is freed once the object itself is deleted along with its dependent objects that were made unreachable from GC roots.

More information about Google Chrome Heap Profiler and memory management you can find here.

There are cases when we don’t need this crazy amount of memory. Or maybe we want to store information in bits, so we don’t need to allocate 8 bytes per number. What can we do?

Embrace Typed Arrays!

There are array-like objects in Javascript which allow working with the binary data. You can simply create them like this:

// This buffer has a size of 8 bytes
const buffer = new ArrayBuffer(8);
console.log(buffer.byteLength) // 8

The thing is that the buffer is not an Array. You can’t access the elements with indexes and do lots of array-related stuff. What you can do with it?

To access the buffer data you need to create a sort of view on top of the data. For instance, you can treat this buffer as UInt8 array or UInt16 array like this:

const view = new Uint8Array(buffer);console.log(view[0]); // 0
console.log(Array.isArray(view)); // false

Just remember that the view is not an Array too.

You can make the following views:

  • Int8Array
  • Uint8Array
  • Uint8ClampedArray
  • Int16Array
  • Uint16Array
  • Int32Array
  • Uint32Array
  • Float32Array
  • Float64Array
  • BigInt64Array
  • BigUint64Array

An interesting thing is that you can make several views for one buffer:

const buffer = new ArrayBuffer(8);const uint8view = new Uint8Array(buffer);
const uint16view = new Uint16Array(buffer);
uint8view[2] = 6;
uint16view[0] = 5;
console.log(uint8view); // Uint8Array(8) [5, 0, 6, 0, 0, 0, 0, 0]
console.log(uint16view); // Uint16Array(4) [5, 6, 0, 0]

Be careful here because you change the same memory buffer.

Packing the data

There are some cases when you need to handle lots of complex data structures (for instance objects with many different fields). In order to save the memory, you can pack them into ArrayBuffer . For instance, you have the following object (this example is for C language):

struct someStruct {
unsigned long id;
char username[16];
float amountDue;
};

It’s quite easy to pack this data structure into ArrayBuffer.

// First of all you need to allocate the memory for the object
const buffer = new ArrayBuffer(24);
// Then you can place your data to the buffer// Here the second argument is the offset within the buffer
// The third argument is the number of the data view units you want to use
const idView = new Uint32Array(buffer, 0, 1);
const usernameView = new Uint8Array(buffer, 4, 16);
const amountDueView = new Float32Array(buffer, 20, 1);

It’s easy to construct your own data structures and use the memory in a more efficient way.

Conclusion

It is useful to know about ArrayBuffer and Typed Arrays. They can help to reduce the memory usage of your application. Still, there are cases when ArrayBuffer comes in handy: Web Workers. You can pass array buffer directly to the web worker without copying the data! However, this is another story…

--

--

Vladislav Lipatov
Vladislav Lipatov

Written by Vladislav Lipatov

Passionate Front-end dev @ The Bricks Inc

No responses yet