Как я могу исправить эту утечку памяти в моем приложении для работы с файлами NodeJS?

#node.js #api #file #express #io

#node.js #API #файл #экспресс #io

Вопрос:

Цель моих приложений на данном этапе — взять входной файл и добавить обычные замены пароля к заданной фразе пароля. Проблема в том, что оно выполняет эту операцию с потенциально большим файлом (более 1 ГБ текста), по одному паролю на строку, а затем добавляет результирующий список потенциальных замен обратно в тот же входной файл через writeStream .

Итак, например, ввод 5_passwords.txt которое содержит:

 password
hello
chicken
bye
bobthebuilder
  

может привести к созданию файла, подобного этому:

 ...
p@s$w0rd
h3ll0
ch1ck3n
8y3
b0bth3bu11d34
...
  

При использовании большого файла использование памяти также увеличивается, но, насколько я понимаю, оно должно оставаться неизменным. Ниже приведены фактические журналы вывода консоли для Memory use: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100 / 100)}MB

Использование памяти в начале: Memory use: 20MB (Вокруг того, что приложение должно использовать)

Использование памяти через 10 секунд: Memory use: 476MB (О-о-о …)

Использование памяти через 30 секунд: Memory use: 1159MB (КАК ВАУ)

Ниже приведен мой соответствующий код:

 const commonReplacesments = {
    a : [
        '4',
        '@'
    ],
    b : [
        '8'
    ],
    c : [
        '(',
        '{',
        '[',
        '<'
    ],
    e : [
        '3'
    ],
    g : [
        '6',
        '9'
    ],
    i : [
        '1',
        '!',
        '|'
    ],
    l : [
        '1',
        '|',
        '7'
    ],
    o : [
        '0'
    ],
    p : [
        '9'
    ],
    r : [
        '4'
    ],
    s : [
        '$',
        '5'
    ],
    t : [
        ' ',
        '7'
    ],
    x : [
        '%'
    ],
    z : [
        '2'
    ]
}

// word ... is the word to generate *some* possible manipulations on using the commonreplacements above
const replaceWord = async (word) => {
    let wordsWithReplacements = []
    for (let i = 0; i < word.length; i  ) {
        let currentWord = word
        if (currentWord[i] in commonReplacesments) {
            for (let j = 0; j < commonReplacesments[currentWord[i]].length; j  ) {
                let replacer = new RegExp(currentWord[i], 'g')
                currentWord = currentWord.replace(replacer, commonReplacesments[currentWord[i][j])
                wordsWithReplacements.push(currentWord)

                // Reset word
                currentWord = word

                console.log(
                    `[${Date.now()}] Memory use: ${Math.round(
                        process.memoryUsage().heapUsed / 1024 / 1024 * 100 / 100
                    )}MB`
                )
            }
        }
    }

    return wordsWithReplacements
}

// passFile ... Is the absolute file path to the password file being manipulated.
const addCommonReplacements = async (passFile) => {
    const readStream = fs.createReadStream(passFile)

    console.log('Adding common replacements to existing phrases in password list...')

    try {
        await util.promisify(stream.pipeline)(async function*() {
            for await (const password of readStream) {
                yield `${await (await replaceWord(`${password}`)).join('n')}`
            }
        }, fs.createWriteStream(passFile, { flags: 'a' }))
    } catch (err) {
        Promise.reject(`ERROR: Failed to write common replacements to file...${err}`)
    }
}
  

Если кто-нибудь может, пожалуйста, предоставить некоторую информацию, чтобы помочь мне решить эту проблему, я был бы очень признателен 🙂

Спасибо!

Ответ №1:

Я не смог запустить ваш код, но я разработал эту упрощенную версию, и, похоже, она работает хорошо (хотя в результате есть некоторые undefined значения, но это связано с функцией замены, в которую я действительно не заглядывал). Вы можете использовать Node.js внутренний пакет readline, который анализирует строку из потока для вас.

 const addCommonReplacements = async passFile => {
    const readStream = fs.createReadStream(passFile)
    const writeStream = fs.createWriteStream('out.txt')

    var rl = readline.createInterface(readStream, writeStream)

    rl.on('line', function (password) {
        writeStream.write(`${replaceWord(password)}n`)
    });
}
  

Кроме того, я не думаю, что это хорошая идея — читать из одного файла и записывать в него!

ПС. Я запускаю скрипт для файла с 65536 строками при максимальном объеме используемой памяти 27 МБ.

Комментарии:

1. Я попробую. Спасибо за ваш пост! Однако мне интересно, почему вы не считаете хорошей идеей читать из одного файла и записывать в него?

2. Этот код на самом деле ничего не записывает. Также просто в качестве примечания replaceWord функция является асинхронной и возвращает список возможных замен, поэтому вы не можете просто записать список непосредственно в writeStream .

3. Кроме того, я почти уверен, что проблема заключается в replaceWord функции, а не в addCommonReplacements function, поскольку, когда я запускаю код без выполнения замен, а просто считываю из файла и добавляю неизмененное содержимое обратно в поток записи, утечки памяти не происходит.