muspe-cli
Version:
MusPE Advanced Framework v2.1.3 - Mobile User-friendly Simple Progressive Engine with Enhanced CLI Tools, Specialized E-Commerce Templates, Material Design 3, Progressive Enhancement, Mobile Optimizations, Performance Analysis, and Enterprise-Grade Develo
737 lines (617 loc) • 20.2 kB
JavaScript
// MusPE Core Framework - Memory Optimizer
class MusPEMemoryOptimizer {
constructor(options = {}) {
this.options = {
enableAutoOptimization: options.enableAutoOptimization !== false,
memoryThreshold: options.memoryThreshold || 0.8, // 80% of available memory
optimizationInterval: options.optimizationInterval || 30000, // 30 seconds
enableMemoryProfiler: options.enableMemoryProfiler !== false,
enableLeakDetection: options.enableLeakDetection !== false,
maxObjectLifetime: options.maxObjectLifetime || 300000, // 5 minutes
enableWeakReferences: options.enableWeakReferences !== false,
...options
};
// Memory tracking
this.memoryStats = {
used: 0,
total: 0,
peak: 0,
optimizations: 0,
leaksDetected: 0,
lastOptimization: null
};
// Object lifecycle tracking
this.objectRegistry = new WeakMap();
this.objectLifecycle = new Map();
this.weakReferences = new WeakMap();
// Component memory tracking
this.componentMemory = new Map();
this.eventListeners = new Map();
this.domObservers = new Map();
// Memory optimization strategies
this.optimizationStrategies = new Map();
// Performance monitoring
this.performanceObserver = null;
this.memoryObserver = null;
// Auto cleanup intervals
this.intervals = new Set();
this.init();
}
init() {
this.registerOptimizationStrategies();
this.startMemoryMonitoring();
this.startAutoOptimization();
this.startLeakDetection();
if (typeof window !== 'undefined') {
this.initializeBrowserOptimizations();
window.MusPEMemoryOptimizer = this;
}
}
// ============ MEMORY MONITORING ============
startMemoryMonitoring() {
if (typeof performance !== 'undefined' && performance.memory) {
const interval = setInterval(() => {
this.updateMemoryStats();
this.checkMemoryThreshold();
}, 5000);
this.intervals.add(interval);
}
// Performance Observer for memory-related metrics
if (typeof PerformanceObserver !== 'undefined') {
try {
this.performanceObserver = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.entryType === 'memory') {
this.handleMemoryEntry(entry);
}
});
});
this.performanceObserver.observe({ entryTypes: ['memory'] });
} catch (error) {
console.warn('Performance Observer not supported:', error);
}
}
}
updateMemoryStats() {
if (typeof performance !== 'undefined' && performance.memory) {
const memory = performance.memory;
this.memoryStats.used = memory.usedJSHeapSize;
this.memoryStats.total = memory.totalJSHeapSize;
this.memoryStats.peak = Math.max(this.memoryStats.peak, this.memoryStats.used);
return {
used: this.memoryStats.used,
total: this.memoryStats.total,
peak: this.memoryStats.peak,
percentage: (this.memoryStats.used / this.memoryStats.total) * 100
};
}
return null;
}
checkMemoryThreshold() {
const stats = this.updateMemoryStats();
if (stats && stats.percentage > (this.options.memoryThreshold * 100)) {
this.triggerOptimization('threshold_exceeded');
}
}
// ============ OBJECT LIFECYCLE MANAGEMENT ============
trackObject(obj, metadata = {}) {
if (!obj || typeof obj !== 'object') return;
const id = this.generateObjectId();
const tracking = {
id,
created: Date.now(),
type: obj.constructor.name,
size: this.estimateObjectSize(obj),
metadata,
lastAccessed: Date.now(),
accessCount: 0,
references: 0
};
this.objectRegistry.set(obj, tracking);
this.objectLifecycle.set(id, {
object: obj,
tracking
});
return id;
}
untrackObject(obj) {
const tracking = this.objectRegistry.get(obj);
if (tracking) {
this.objectLifecycle.delete(tracking.id);
this.objectRegistry.delete(obj);
}
}
accessObject(obj) {
const tracking = this.objectRegistry.get(obj);
if (tracking) {
tracking.lastAccessed = Date.now();
tracking.accessCount++;
}
}
createWeakReference(obj) {
if (!this.options.enableWeakReferences) return obj;
const weakRef = new WeakRef(obj);
this.weakReferences.set(obj, weakRef);
return weakRef;
}
// ============ COMPONENT MEMORY MANAGEMENT ============
trackComponent(component, name) {
const componentId = this.generateObjectId();
const memoryUsage = {
id: componentId,
name,
created: Date.now(),
domElements: 0,
eventListeners: 0,
observers: 0,
children: new Set(),
parent: null,
memorySize: 0
};
this.componentMemory.set(component, memoryUsage);
this.trackObject(component, { type: 'component', name });
return componentId;
}
trackEventListener(component, element, event, handler) {
const componentMemory = this.componentMemory.get(component);
if (componentMemory) {
componentMemory.eventListeners++;
}
const listenerId = this.generateObjectId();
this.eventListeners.set(listenerId, {
component,
element,
event,
handler,
created: Date.now()
});
return listenerId;
}
trackDOMObserver(component, observer, target) {
const componentMemory = this.componentMemory.get(component);
if (componentMemory) {
componentMemory.observers++;
}
const observerId = this.generateObjectId();
this.domObservers.set(observerId, {
component,
observer,
target,
created: Date.now()
});
return observerId;
}
cleanupComponent(component) {
const componentMemory = this.componentMemory.get(component);
if (!componentMemory) return;
// Cleanup event listeners
for (const [id, listener] of this.eventListeners.entries()) {
if (listener.component === component) {
try {
listener.element.removeEventListener(listener.event, listener.handler);
this.eventListeners.delete(id);
} catch (error) {
console.warn('Error removing event listener:', error);
}
}
}
// Cleanup DOM observers
for (const [id, observer] of this.domObservers.entries()) {
if (observer.component === component) {
try {
observer.observer.disconnect();
this.domObservers.delete(id);
} catch (error) {
console.warn('Error disconnecting observer:', error);
}
}
}
// Cleanup children
componentMemory.children.forEach(child => {
this.cleanupComponent(child);
});
// Remove from tracking
this.componentMemory.delete(component);
this.untrackObject(component);
}
// ============ OPTIMIZATION STRATEGIES ============
registerOptimizationStrategies() {
// DOM cleanup strategy
this.optimizationStrategies.set('dom_cleanup', {
name: 'DOM Cleanup',
priority: 1,
execute: () => this.optimizeDOMMemory()
});
// Event listener cleanup
this.optimizationStrategies.set('event_cleanup', {
name: 'Event Listener Cleanup',
priority: 2,
execute: () => this.optimizeEventListeners()
});
// Cache optimization
this.optimizationStrategies.set('cache_optimization', {
name: 'Cache Optimization',
priority: 3,
execute: () => this.optimizeCache()
});
// Object lifecycle cleanup
this.optimizationStrategies.set('object_cleanup', {
name: 'Object Lifecycle Cleanup',
priority: 4,
execute: () => this.optimizeObjectLifecycle()
});
// Garbage collection hint
this.optimizationStrategies.set('gc_hint', {
name: 'Garbage Collection Hint',
priority: 5,
execute: () => this.triggerGarbageCollection()
});
}
optimizeDOMMemory() {
let optimized = 0;
// Remove detached DOM elements
if (typeof document !== 'undefined') {
const elements = document.querySelectorAll('*');
elements.forEach(element => {
if (!element.isConnected && element.parentNode === null) {
try {
element.remove();
optimized++;
} catch (error) {
// Element already removed
}
}
});
}
return { optimized, type: 'dom_elements' };
}
optimizeEventListeners() {
let optimized = 0;
const now = Date.now();
// Remove old event listeners
for (const [id, listener] of this.eventListeners.entries()) {
if (now - listener.created > this.options.maxObjectLifetime) {
try {
listener.element.removeEventListener(listener.event, listener.handler);
this.eventListeners.delete(id);
optimized++;
} catch (error) {
// Element or handler no longer valid
this.eventListeners.delete(id);
}
}
}
return { optimized, type: 'event_listeners' };
}
optimizeCache() {
let optimized = 0;
// Trigger cache optimization if available
if (typeof window !== 'undefined' && window.MusPECache) {
const beforeSize = window.MusPECache.getCurrentMemoryUsage();
window.MusPECache.evictExpired();
window.MusPECache.evictLeastUsed();
const afterSize = window.MusPECache.getCurrentMemoryUsage();
optimized = beforeSize - afterSize;
}
return { optimized, type: 'cache_memory' };
}
optimizeObjectLifecycle() {
let optimized = 0;
const now = Date.now();
// Clean up old objects
for (const [id, entry] of this.objectLifecycle.entries()) {
if (now - entry.tracking.created > this.options.maxObjectLifetime) {
this.objectLifecycle.delete(id);
optimized++;
}
}
// Clean up weak references
for (const [obj, weakRef] of this.weakReferences.entries()) {
if (weakRef.deref() === undefined) {
this.weakReferences.delete(obj);
optimized++;
}
}
return { optimized, type: 'object_lifecycle' };
}
triggerGarbageCollection() {
// Force garbage collection if available (mainly for development)
if (typeof window !== 'undefined' && window.gc) {
try {
window.gc();
return { optimized: 1, type: 'garbage_collection' };
} catch (error) {
console.warn('Manual garbage collection not available');
}
}
// Alternative: create memory pressure to encourage GC
let temp = [];
for (let i = 0; i < 1000; i++) {
temp.push(new Array(1000).fill(Math.random()));
}
temp = null;
return { optimized: 1, type: 'gc_pressure' };
}
// ============ AUTO OPTIMIZATION ============
startAutoOptimization() {
if (!this.options.enableAutoOptimization) return;
const interval = setInterval(() => {
this.triggerOptimization('auto');
}, this.options.optimizationInterval);
this.intervals.add(interval);
}
triggerOptimization(reason = 'manual') {
const startTime = performance.now();
const startMemory = this.updateMemoryStats();
console.log(`🔧 MusPE Memory Optimization triggered: ${reason}`);
const results = [];
// Execute optimization strategies by priority
const strategies = Array.from(this.optimizationStrategies.values())
.sort((a, b) => a.priority - b.priority);
for (const strategy of strategies) {
try {
const result = strategy.execute();
results.push({
strategy: strategy.name,
...result
});
} catch (error) {
console.error(`Optimization strategy failed: ${strategy.name}`, error);
}
}
const endTime = performance.now();
const endMemory = this.updateMemoryStats();
this.memoryStats.optimizations++;
this.memoryStats.lastOptimization = Date.now();
const optimizationReport = {
reason,
duration: endTime - startTime,
memoryBefore: startMemory,
memoryAfter: endMemory,
memorySaved: startMemory ? startMemory.used - endMemory.used : 0,
strategies: results,
timestamp: new Date().toISOString()
};
console.log('📊 Memory Optimization Complete:', optimizationReport);
// Emit optimization event
if (typeof window !== 'undefined' && window.dispatchEvent) {
window.dispatchEvent(new CustomEvent('muspe:memory:optimized', {
detail: optimizationReport
}));
}
return optimizationReport;
}
// ============ LEAK DETECTION ============
startLeakDetection() {
if (!this.options.enableLeakDetection) return;
const interval = setInterval(() => {
this.detectMemoryLeaks();
}, 60000); // Check every minute
this.intervals.add(interval);
}
detectMemoryLeaks() {
const leaks = [];
const now = Date.now();
// Check for long-lived objects
for (const [id, entry] of this.objectLifecycle.entries()) {
const age = now - entry.tracking.created;
const timeSinceAccess = now - entry.tracking.lastAccessed;
if (age > this.options.maxObjectLifetime &&
timeSinceAccess > this.options.maxObjectLifetime * 0.5) {
leaks.push({
type: 'long_lived_object',
id,
age,
timeSinceAccess,
objectType: entry.tracking.type,
size: entry.tracking.size
});
}
}
// Check for orphaned event listeners
for (const [id, listener] of this.eventListeners.entries()) {
try {
if (!listener.element.isConnected) {
leaks.push({
type: 'orphaned_event_listener',
id,
age: now - listener.created,
element: listener.element.tagName,
event: listener.event
});
}
} catch (error) {
// Element no longer exists
leaks.push({
type: 'invalid_event_listener',
id,
age: now - listener.created
});
}
}
// Check for disconnected observers
for (const [id, observer] of this.domObservers.entries()) {
try {
if (!observer.target.isConnected) {
leaks.push({
type: 'orphaned_observer',
id,
age: now - observer.created
});
}
} catch (error) {
leaks.push({
type: 'invalid_observer',
id,
age: now - observer.created
});
}
}
if (leaks.length > 0) {
this.memoryStats.leaksDetected += leaks.length;
console.warn('🚨 Memory leaks detected:', leaks);
// Auto-cleanup some types of leaks
this.cleanupDetectedLeaks(leaks);
// Emit leak detection event
if (typeof window !== 'undefined' && window.dispatchEvent) {
window.dispatchEvent(new CustomEvent('muspe:memory:leaks', {
detail: { leaks, timestamp: new Date().toISOString() }
}));
}
}
return leaks;
}
cleanupDetectedLeaks(leaks) {
leaks.forEach(leak => {
try {
switch (leak.type) {
case 'orphaned_event_listener':
case 'invalid_event_listener':
this.eventListeners.delete(leak.id);
break;
case 'orphaned_observer':
case 'invalid_observer':
this.domObservers.delete(leak.id);
break;
case 'long_lived_object':
this.objectLifecycle.delete(leak.id);
break;
}
} catch (error) {
console.warn('Error cleaning up leak:', error);
}
});
}
// ============ BROWSER OPTIMIZATIONS ============
initializeBrowserOptimizations() {
// Page visibility API for aggressive cleanup
if (typeof document !== 'undefined') {
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.triggerOptimization('page_hidden');
}
});
}
// Memory pressure event handling
if (typeof window !== 'undefined' && 'memory' in performance) {
window.addEventListener('beforeunload', () => {
this.triggerOptimization('page_unload');
});
}
// Intersection Observer for element cleanup
if (typeof IntersectionObserver !== 'undefined') {
this.setupIntersectionObserver();
}
}
setupIntersectionObserver() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) {
// Element is out of view, potential cleanup candidate
const component = this.findComponentByElement(entry.target);
if (component) {
this.markForOptimization(component);
}
}
});
}, {
rootMargin: '100px',
threshold: 0
});
this.intersectionObserver = observer;
}
// ============ UTILITIES ============
generateObjectId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
estimateObjectSize(obj) {
try {
return JSON.stringify(obj).length * 2; // Rough estimate
} catch (error) {
return 1024; // Default estimate
}
}
findComponentByElement(element) {
for (const [component, memory] of this.componentMemory.entries()) {
if (component.element === element) {
return component;
}
}
return null;
}
markForOptimization(component) {
const componentMemory = this.componentMemory.get(component);
if (componentMemory) {
componentMemory.markedForOptimization = Date.now();
}
}
// ============ PUBLIC API ============
getMemoryStats() {
return {
...this.memoryStats,
current: this.updateMemoryStats(),
objects: {
tracked: this.objectLifecycle.size,
components: this.componentMemory.size,
eventListeners: this.eventListeners.size,
observers: this.domObservers.size,
weakReferences: this.weakReferences.size
}
};
}
manualOptimization() {
return this.triggerOptimization('manual');
}
enableStrategy(strategyName) {
const strategy = this.optimizationStrategies.get(strategyName);
if (strategy) {
strategy.enabled = true;
}
}
disableStrategy(strategyName) {
const strategy = this.optimizationStrategies.get(strategyName);
if (strategy) {
strategy.enabled = false;
}
}
addCustomStrategy(name, strategyFn, priority = 10) {
this.optimizationStrategies.set(name, {
name,
priority,
execute: strategyFn,
enabled: true
});
}
debugMemory() {
console.group('MusPE Memory Optimizer Debug');
console.table(this.getMemoryStats());
console.log('Tracked Objects:', Array.from(this.objectLifecycle.entries()));
console.log('Component Memory:', Array.from(this.componentMemory.entries()));
console.log('Event Listeners:', Array.from(this.eventListeners.entries()));
console.log('DOM Observers:', Array.from(this.domObservers.entries()));
console.groupEnd();
}
destroy() {
// Clear all intervals
this.intervals.forEach(interval => clearInterval(interval));
this.intervals.clear();
// Disconnect observers
if (this.performanceObserver) {
this.performanceObserver.disconnect();
}
if (this.intersectionObserver) {
this.intersectionObserver.disconnect();
}
// Final cleanup
this.objectLifecycle.clear();
this.componentMemory.clear();
this.eventListeners.clear();
this.domObservers.clear();
this.weakReferences.clear();
}
}
// Export for module systems
if (typeof module !== 'undefined' && module.exports) {
module.exports = MusPEMemoryOptimizer;
}
export default MusPEMemoryOptimizer;