javascript
examples
examples.js⚡javascript
/**
* ============================================
* 6.5 MEMORY MANAGEMENT - EXAMPLES
* ============================================
*/
/**
* EXAMPLE 1: Stack vs Heap - Primitives
* -------------------------------------
* Primitives are copied by value
*/
console.log('=== Example 1: Primitives (Stack) ===');
let a = 10;
let b = a; // Copy of value
b = 20;
console.log('a:', a); // 10 (unchanged)
console.log('b:', b); // 20
// Each variable has its own copy in stack memory
/**
* EXAMPLE 2: Stack vs Heap - Objects
* ----------------------------------
* Objects are passed by reference
*/
console.log('\n=== Example 2: Objects (Heap) ===');
let obj1 = { value: 10 };
let obj2 = obj1; // Copy of reference (same object!)
obj2.value = 20;
console.log('obj1.value:', obj1.value); // 20 (changed!)
console.log('obj2.value:', obj2.value); // 20
console.log('obj1 === obj2:', obj1 === obj2); // true (same reference)
/**
* EXAMPLE 3: Creating a Copy
* --------------------------
* How to avoid sharing references
*/
console.log('\n=== Example 3: Creating Copies ===');
const original = { name: 'Alice', age: 30 };
// Shallow copy
const shallowCopy = { ...original };
shallowCopy.name = 'Bob';
console.log('original.name:', original.name); // "Alice"
console.log('shallowCopy.name:', shallowCopy.name); // "Bob"
// Deep copy (for nested objects)
const nested = { user: { name: 'Alice' } };
const deepCopy = JSON.parse(JSON.stringify(nested));
deepCopy.user.name = 'Bob';
console.log('nested.user.name:', nested.user.name); // "Alice"
console.log('deepCopy.user.name:', deepCopy.user.name); // "Bob"
/**
* EXAMPLE 4: Garbage Collection Basics
* ------------------------------------
* Objects are collected when unreachable
*/
console.log('\n=== Example 4: Garbage Collection ===');
function createObject() {
const localObj = { data: new Array(1000).fill('x') };
console.log('Object created with', localObj.data.length, 'items');
// localObj goes out of scope here - eligible for GC
}
createObject();
console.log('Function returned - object can be garbage collected');
/**
* EXAMPLE 5: Reference Keeping Object Alive
* -----------------------------------------
* Object stays in memory if referenced
*/
console.log('\n=== Example 5: Keeping Objects Alive ===');
let keepAlive = null;
function createAndKeep() {
const obj = { data: 'important data' };
keepAlive = obj; // Reference kept!
console.log('Object created and referenced');
}
createAndKeep();
console.log('Function returned but object still alive:', keepAlive.data);
// To allow GC:
// keepAlive = null;
/**
* EXAMPLE 6: Memory Leak - Accidental Global
* ------------------------------------------
* Variables without declaration become global
*/
console.log('\n=== Example 6: Accidental Global ===');
function leakyFunction() {
// In non-strict mode, this creates a global variable!
// leakedVar = "I'm accidentally global";
// CORRECT way:
let properVar = "I'm properly scoped";
console.log('Proper variable:', properVar);
}
leakyFunction();
// console.log(leakedVar); // Would exist if we created the leak!
/**
* EXAMPLE 7: Memory Leak - Forgotten Timer
* ----------------------------------------
* Timers keep references alive
*/
console.log('\n=== Example 7: Timer Leak ===');
function timerLeak() {
const largeData = new Array(10000).fill('data');
// This timer keeps largeData in memory!
const timerId = setInterval(() => {
console.log('Timer running, data length:', largeData.length);
}, 1000);
// Return cleanup function
return () => {
clearInterval(timerId);
console.log('Timer cleaned up');
};
}
const cleanup = timerLeak();
// Call cleanup() when done to prevent leak
setTimeout(cleanup, 2500); // Clean up after a few ticks
/**
* EXAMPLE 8: Closure Memory
* -------------------------
* Closures can hold more memory than expected
*/
console.log('\n=== Example 8: Closure Memory ===');
function createClosures() {
const hugeArray = new Array(100000).fill('x');
const smallValue = hugeArray.length;
// BAD: Keeps entire array in memory
const badClosure = () => {
return hugeArray[0];
};
// GOOD: Only keeps what's needed
const goodClosure = () => {
return smallValue;
};
return { badClosure, goodClosure };
}
const closures = createClosures();
console.log('Bad closure result:', closures.badClosure());
console.log('Good closure result:', closures.goodClosure());
// badClosure keeps the 100k array alive!
/**
* EXAMPLE 9: WeakMap for Object Metadata
* --------------------------------------
* WeakMap allows garbage collection of keys
*/
console.log('\n=== Example 9: WeakMap ===');
const metadata = new WeakMap();
function processObject(obj) {
if (!metadata.has(obj)) {
metadata.set(obj, {
processedAt: Date.now(),
count: 0,
});
}
const data = metadata.get(obj);
data.count++;
return data;
}
let myObj = { name: 'test' };
console.log('First process:', processObject(myObj));
console.log('Second process:', processObject(myObj));
// When myObj is set to null, the metadata is also GC'd
// myObj = null;
/**
* EXAMPLE 10: WeakSet for Tracking
* --------------------------------
* Track objects without preventing GC
*/
console.log('\n=== Example 10: WeakSet ===');
const processed = new WeakSet();
function processOnce(obj) {
if (processed.has(obj)) {
console.log('Already processed:', obj.id);
return false;
}
processed.add(obj);
console.log('Processing:', obj.id);
return true;
}
const item1 = { id: 1 };
const item2 = { id: 2 };
processOnce(item1); // Processing: 1
processOnce(item2); // Processing: 2
processOnce(item1); // Already processed: 1
/**
* EXAMPLE 11: Object Pooling
* --------------------------
* Reuse objects to reduce allocations
*/
console.log('\n=== Example 11: Object Pooling ===');
class ParticlePool {
constructor(size) {
this.particles = [];
for (let i = 0; i < size; i++) {
this.particles.push({
x: 0,
y: 0,
vx: 0,
vy: 0,
active: false,
});
}
console.log(`Pool created with ${size} particles`);
}
acquire() {
const particle = this.particles.find((p) => !p.active);
if (particle) {
particle.active = true;
return particle;
}
console.log('Pool exhausted!');
return null;
}
release(particle) {
particle.x = 0;
particle.y = 0;
particle.vx = 0;
particle.vy = 0;
particle.active = false;
}
getActiveCount() {
return this.particles.filter((p) => p.active).length;
}
}
const pool = new ParticlePool(5);
const p1 = pool.acquire();
p1.x = 100;
const p2 = pool.acquire();
p2.x = 200;
console.log('Active particles:', pool.getActiveCount());
pool.release(p1);
console.log('After release:', pool.getActiveCount());
/**
* EXAMPLE 12: Nullifying References
* ---------------------------------
* Explicitly clear large objects
*/
console.log('\n=== Example 12: Nullifying References ===');
function processLargeData() {
let data = new Array(100000).fill('important');
// Process the data
const result = data.length;
// Clear the reference
data = null;
console.log('Data processed, reference cleared');
return result;
}
console.log('Result:', processLargeData());
/**
* EXAMPLE 13: Event Listener Cleanup
* ----------------------------------
* Properly remove event listeners
*/
console.log('\n=== Example 13: Event Listener Cleanup ===');
class Component {
constructor(id) {
this.id = id;
this.data = new Array(1000).fill('data');
this.boundHandler = this.handleEvent.bind(this);
// Simulate adding listener
console.log(`Component ${id}: Adding event listener`);
// In browser: element.addEventListener('click', this.boundHandler);
}
handleEvent(event) {
console.log(`Component ${this.id}: Handling event`);
}
destroy() {
// Remove listener to prevent leak
console.log(`Component ${this.id}: Removing event listener`);
// In browser: element.removeEventListener('click', this.boundHandler);
this.data = null;
}
}
const comp = new Component(1);
// When done:
comp.destroy();
/**
* EXAMPLE 14: Chunked Processing
* ------------------------------
* Process data in chunks to manage memory
*/
console.log('\n=== Example 14: Chunked Processing ===');
async function processInChunks(items, chunkSize, processor) {
console.log(`Processing ${items.length} items in chunks of ${chunkSize}`);
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
// Process chunk
for (const item of chunk) {
processor(item);
}
console.log(
`Processed items ${i} to ${Math.min(i + chunkSize, items.length)}`
);
// Yield to allow GC and other operations
await new Promise((resolve) => setTimeout(resolve, 0));
}
console.log('All chunks processed');
}
const items = Array.from({ length: 100 }, (_, i) => i);
processInChunks(items, 25, (item) => {
// Process each item
});
/**
* EXAMPLE 15: Measuring Memory (Node.js)
* --------------------------------------
* Check memory usage
*/
console.log('\n=== Example 15: Memory Measurement ===');
function getMemoryUsage() {
if (typeof process !== 'undefined' && process.memoryUsage) {
const usage = process.memoryUsage();
return {
heapUsed: Math.round(usage.heapUsed / 1024 / 1024) + ' MB',
heapTotal: Math.round(usage.heapTotal / 1024 / 1024) + ' MB',
};
}
return 'Memory API not available (browser)';
}
console.log('Memory before:', getMemoryUsage());
// Allocate some memory
const bigArray = new Array(1000000).fill('x');
console.log('Memory after allocation:', getMemoryUsage());
// Clear reference
// bigArray = null;
// In Node.js with --expose-gc flag, you could call global.gc()
/**
* EXAMPLE 16: Circular References
* -------------------------------
* Modern GC handles circular references
*/
console.log('\n=== Example 16: Circular References ===');
function createCircular() {
const objA = { name: 'A' };
const objB = { name: 'B' };
// Circular reference
objA.ref = objB;
objB.ref = objA;
console.log('Created circular reference');
return { objA, objB };
}
const circular = createCircular();
console.log('A references B:', circular.objA.ref.name);
console.log('B references A:', circular.objB.ref.name);
// Modern GC (mark-and-sweep) can handle this!
// Both objects will be collected when 'circular' is nulled
/**
* EXAMPLE 17: Memory-Efficient Strings
* ------------------------------------
* String interning and memory
*/
console.log('\n=== Example 17: String Memory ===');
// Identical strings may share memory (interning)
const str1 = 'hello';
const str2 = 'hello';
console.log('str1 === str2:', str1 === str2); // true, possibly same memory
// But string operations create new strings
const str3 = 'hel' + 'lo';
console.log('str1 === str3:', str1 === str3); // true, engine may optimize
// Large string concatenation - use array join
const parts = [];
for (let i = 0; i < 1000; i++) {
parts.push(`item${i}`);
}
const result = parts.join(', ');
console.log('Joined string length:', result.length);
/**
* EXAMPLE 18: Map vs Object Memory
* --------------------------------
* Different memory characteristics
*/
console.log('\n=== Example 18: Map vs Object ===');
// Objects: Good for fixed known keys
const objStore = {
key1: 'value1',
key2: 'value2',
};
// Maps: Better for dynamic keys, more memory for metadata
const mapStore = new Map([
['key1', 'value1'],
['key2', 'value2'],
]);
// WeakMap: Keys can be garbage collected
const weakStore = new WeakMap();
let key = { id: 1 };
weakStore.set(key, 'value');
console.log('Object keys:', Object.keys(objStore));
console.log('Map size:', mapStore.size);
console.log('WeakMap has key:', weakStore.has(key));
console.log('\n=== End of Examples ===');