blob: 865284eb466468c95d632875a89210776ae62ed9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// Usually, Linux systems have a page size of 4096 byte
// The actual value can be obtained with `getconf PAGESIZE`
const pageSizeInB = 4096;
// This array will be used to store all allocated blocks
// such that they won't be garbage collected
let blocks = [];
// Allocates a byte array that has page size
function allocateMemoryBlock(sizeInB) {
return new Uint8Array(sizeInB);
}
// Returns a random number between 0 (inclusive) and
// the specified value maxExclusive (exclusive)
function randomUnsigned(maxExclusive) {
return Math.floor(Math.random() * maxExclusive);
}
// Fills the first 4 bytes of the passed byte array with random
// numbers
function fillMemoryPage(byteArray) {
for (let i = 0; (i < 4) && (i < pageSizeInB); i++) {
byteArray[i] = randomUnsigned(256);
}
}
// Consumes the specified amount of physical memory
// * The memory is allocated in smaller blocks instead of
// allocating one large block of memory to prevent
// virtual OOM
// * Size of allocated blocks is a multiple of page size
// * The number of allocated blocks has an upper bound
// because a reference to each block is stored in an
// array. If the number of blocks gets too high, the
// resulting array grows so large that its contribution
// to memory consumption causes trouble.
// For this reason, the block size is adjusted to
// limit the number of blocks. The resulting allocation
// granularity can cause a slight over-consumption of
// memory. That's why the upper limit must be selected
// carefully.
// * Fill randomly to prevent memory deduplication
function eat(memoryInMiB) {
const memoryInB = memoryInMiB * 1024 * 1024;
const memoryInPages = Math.ceil(memoryInB / pageSizeInB);
console.log('helloEatMemory: memoryInB=' + memoryInB + ', memoryInPages=' + memoryInPages);
let blockSizeInB = pageSizeInB;
let memoryInBlocks = memoryInPages;
let pagesPerBlock = 1;
const maxBlocks = 8192;
if (memoryInPages > maxBlocks) {
pagesPerBlock = Math.ceil(memoryInB / (maxBlocks * pageSizeInB));
blockSizeInB = pagesPerBlock * pageSizeInB;
memoryInBlocks = Math.ceil(memoryInB / blockSizeInB);
}
console.log('helloEatMemory: pagesPerBlock=' + pagesPerBlock + ', blockSizeInB=' + blockSizeInB + ', memoryInBlocks=' + memoryInBlocks);
for (let b = 0; b < memoryInBlocks; b++) {
let byteArray = allocateMemoryBlock(blockSizeInB);
fillMemoryPage(byteArray);
blocks.push(byteArray);
}
console.log('helloEatMemory: blocks.length=' + blocks.length);
}
function main(msg) {
console.log('helloEatMemory: memory ' + msg.payload + 'MB');
global.gc();
eat(msg.payload);
console.log('helloEatMemory: completed allocating memory');
console.log(process.memoryUsage());
// This Node.js code is invoked as Apache OW action
// We need to explicitly clear the array such that all
// allocated memory gets garbage collected and
// we have a "fresh" instance on next invocation
// Clean up after ourselves such that the warm container
// does not keep memory
blocks = [];
global.gc();
return {msg: 'OK, buffer of size ' + msg.payload + ' MB has been filled.'};
}