Node.jsで5MB以上の文字列を扱うことができない問題

2024-08-15
山下 徳光
#
#
#

Node.jsで5MBを超える文字列を扱うことができない問題とその解決方法

概要

Node.jsを使っていると、5MBを超える文字列を扱う際に問題が発生することがあります。この問題は、大量のデータを一度にメモリに読み込もうとする際に起こります。特に、サーバーから大きなJSON文字列を受信する際に顕著です。本記事では、この問題の原因と解決方法について解説します。

現象

Node.jsで5MBを超える文字列を扱おうとすると、次のようなエラーが発生することがあります。

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

このエラーは、Node.jsがメモリの限界を超えてデータを処理しようとする際に発生します。大きなデータを一度に読み込むと、メモリが不足し、結果としてアプリケーションがクラッシュしてしまいます。

原因

この問題の主な原因は、Node.jsが単一スレッドで動作し、メモリ管理がJavaScriptエンジン(V8)によって行われているためです。大量のデータを一度に処理しようとすると、メモリの使用量が急増し、ガベージコレクションが追いつかなくなります。その結果、メモリ不足が発生します。

解決方法

この問題を解決するための最良の方法は、バイナリデータとストリームを活用してデータを分割して処理することです。ストリームを使用することで、大量のデータを一度にメモリに読み込むのではなく、データを小さなチャンクに分割して処理できます。以下に、サーバーから5MBを超えるJSON文字列を受信する際に、ストリーム処理を行うサンプルコードを示します。


const axios = require('axios');
const { chain } = require('stream-chain');
const { parser } = require('stream-json');
const { pick } = require('stream-json/filters/Pick');
const { streamArray } = require('stream-json/streamers/StreamArray');

async function fetchAndProcessJson(url) {
  try {
    const response = await axios({
      method: 'get',
      url: url,
      responseType: 'stream' // レスポンスをストリームとして取得
    });

    const pipeline = chain([
      response.data, // axiosのレスポンスストリームを使用
      parser(),
      pick({ filter: 'contents_array' }), // 'contents_array'キーを抽出
      streamArray() // 配列の各要素をストリーム処理
    ]);

    pipeline.on('data', ({ key, value }) => {
      // valueはcontents_arrayの各要素
      processObject(value);
    });

    pipeline.on('end', () => {
      console.log('Finished processing the JSON stream.');
    });

    pipeline.on('error', (err) => {
      console.error('Error processing the JSON stream:', err);
    });

  } catch (error) {
    console.error('Error fetching the JSON:', error);
  }
}

function processObject(obj) {
  // オブジェクトごとの処理を行う関数
  console.log(obj);
}

// サンプルのURLを指定して関数を呼び出す
const url = '';
fetchAndProcessJson(url);


このコードでは、axiosを使用してサーバーからJSONデータをストリームとして取得し、stream-jsonライブラリを使用してデータを分割して処理しています。stream-jsonは、大きなJSONデータを小さなチャンクに分割し、それぞれを順次処理することを可能にします。

まとめ

Node.jsで5MBを超える文字列を扱う際に発生するメモリ不足の問題は、ストリームを活用することで解決できます。大きなデータを一度に読み込むのではなく、ストリームを使用してデータを小さなチャンクに分割し、順次処理することでメモリの使用量を抑えることができます。この方法を活用することで、Node.jsアプリケーションのパフォーマンスと安定性を向上させることができます。

株式会社Grandreamでは、フルリモートであなたのスキルを活かし、活躍できるエンジニアを募集しております。 詳しくは採用ページをご確認いただき、お気軽にお問い合わせください。

株式会社グランドリームでは、AWSを駆使した開発からUI/UXデザインまで、Webアプリケーションに関するすべての要望に応えます。
まずは一度お気軽にご相談ください。

お問い合わせはこちら