UNPKG

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
// 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;