Các bài cùng serial: Tối ưu xử lý ảnh dung lượng lớn trong Node.js
- Tối ưu xử lý bộ nhớ khi xử lý ảnh trong Node.js
- Cách giải phóng bộ nhớ sau mỗi ảnh được xử lý khi dùng sharp trong Node.js
- Cách giải phóng bộ nhớ sau mỗi lần trim ảnh với thư viện trim-image trong Node.js(bạn đang xem)
Bạn đang sử dụng thư viện trim-image
trong Node.js và gặp lỗi full bộ nhớ khi xử lý ảnh dung lượng lớn hoặc số lượng nhiều. Dưới đây là các cách giúp bạn giải phóng bộ nhớ sau mỗi lần trim ảnh.
🔹 1. Giải phóng bộ nhớ bằng global.gc()
Nếu Node.js được chạy với cờ --expose-gc
, bạn có thể gọi global.gc()
sau mỗi lần xử lý ảnh.
✅ Cách làm
Chạy lệnh với --expose-gc
:
node --expose-gc script.js
Và trong code:
const trimImage = require('trim-image');
const fs = require('fs');
async function processImage(input, output) {
return new Promise((resolve, reject) => {
trimImage(input, output, (err) => {
if (err) return reject(err);
console.log(`✅ Trimmed: ${output}`);
// Giải phóng bộ nhớ
if (global.gc) global.gc();
resolve();
});
});
}
async function processImages(images) {
for (const image of images) {
await processImage(image.input, image.output);
}
}
const images = [
{ input: 'input1.jpg', output: 'output1.jpg' },
{ input: 'input2.jpg', output: 'output2.jpg' }
];
processImages(images);
🚀 Lợi ích: Giúp thu hồi bộ nhớ không cần thiết.
🔹 2. Hạn chế xử lý đồng thời bằng for...of
Nếu bạn xử lý nhiều ảnh cùng lúc, bộ nhớ có thể bị tràn. Hãy xử lý ảnh tuần tự bằng for...of
thay vì Promise.all()
.
❌ Cách KHÔNG NÊN DÙNG (có thể gây full bộ nhớ):
await Promise.all(images.map(image => processImage(image.input, image.output)));
Thay vào đó, hãy dùng for...of
:
for (const image of images) {
await processImage(image.input, image.output);
}
🚀 Lợi ích: Hạn chế số lượng ảnh được xử lý cùng lúc.
🔹 3. Giới hạn số ảnh xử lý đồng thời bằng p-limit
Nếu bạn muốn xử lý nhiều ảnh nhưng không quá tải bộ nhớ, hãy sử dụng thư viện p-limit
để giới hạn số lượng ảnh chạy cùng lúc.
✅ Cách làm
Cài đặt p-limit
:
npm install p-limit
Sửa code:
const pLimit = require('p-limit');
const trimImage = require('trim-image');
const limit = pLimit(2); // Chỉ xử lý tối đa 2 ảnh cùng lúc
async function processImage(input, output) {
return new Promise((resolve, reject) => {
trimImage(input, output, (err) => {
if (err) return reject(err);
console.log(`✅ Trimmed: ${output}`);
resolve();
});
});
}
async function processImages(images) {
const tasks = images.map(image => limit(() => processImage(image.input, image.output)));
await Promise.all(tasks);
}
const images = [
{ input: 'input1.jpg', output: 'output1.jpg' },
{ input: 'input2.jpg', output: 'output2.jpg' },
{ input: 'input3.jpg', output: 'output3.jpg' },
{ input: 'input4.jpg', output: 'output4.jpg' }
];
processImages(images);
🚀 Lợi ích: Chỉ chạy tối đa 2 ảnh cùng lúc, tránh full bộ nhớ.
🔹 4. Dùng setImmediate()
để giải phóng event loop
Thêm setImmediate()
vào vòng lặp để giải phóng tài nguyên giữa mỗi lần xử lý ảnh.
✅ Cách làm
async function processImages(images) {
for (const image of images) {
await processImage(image.input, image.output);
await new Promise(resolve => setImmediate(resolve)); // Giải phóng tài nguyên
}
}
🚀 Lợi ích: Tránh nghẽn bộ nhớ khi xử lý quá nhiều ảnh.
🔹 5. Sử dụng Worker Threads để xử lý song song mà không quá tải
Khi cần xử lý ảnh song song, bạn có thể dùng Worker Threads để tránh làm đầy bộ nhớ chính.
✅ Cách làm
Tạo file worker.js
:
const { parentPort, workerData } = require('worker_threads');
const trimImage = require('trim-image');
trimImage(workerData.input, workerData.output, (err) => {
parentPort.postMessage(err ? { error: err } : { success: workerData.output });
});
Tạo file script.js
:
const { Worker } = require('worker_threads');
const images = [
{ input: 'input1.jpg', output: 'output1.jpg' },
{ input: 'input2.jpg', output: 'output2.jpg' },
{ input: 'input3.jpg', output: 'output3.jpg' },
{ input: 'input4.jpg', output: 'output4.jpg' }
];
const MAX_WORKERS = 2; // Chỉ xử lý tối đa 2 ảnh cùng lúc
let activeWorkers = 0;
let index = 0;
function runWorker() {
if (index >= images.length) return;
const image = images[index++];
activeWorkers++;
const worker = new Worker('./worker.js', { workerData: image });
worker.on('message', msg => {
if (msg.success) console.log(`✅ Trimmed: ${msg.success}`);
else console.error(`❌ Error: ${msg.error}`);
activeWorkers--;
runWorker(); // Tiếp tục với ảnh tiếp theo
});
worker.on('error', err => {
console.error(`❌ Worker error: ${err}`);
activeWorkers--;
runWorker();
});
}
// Khởi động số worker ban đầu
for (let i = 0; i < MAX_WORKERS; i++) {
runWorker();
}
🚀 Lợi ích: Xử lý ảnh song song nhưng không quá tải bộ nhớ.
✅ Tóm lại, bạn nên làm gì?
Nếu gặp lỗi full bộ nhớ khi trim ảnh với trim-image
, hãy thử các cách sau:
- Giải phóng bộ nhớ thủ công bằng
global.gc()
(Chạy với--expose-gc
). - Xử lý ảnh tuần tự bằng
for...of
thay vìPromise.all()
. - Giới hạn số lượng ảnh xử lý đồng thời bằng
p-limit
. - Dùng
setImmediate()
để giải phóng tài nguyên giữa mỗi lần xử lý. - Sử dụng Worker Threads để xử lý song song nhưng tránh quá tải.
📌 Cách tối ưu nhất: Nếu bạn xử lý ảnh số lượng lớn, hãy kết hợp Worker Threads với p-limit để đạt hiệu suất cao mà không tốn quá nhiều bộ nhớ.