UNPKG

nativescript-matrix-sdk

Version:

Native Matrix SDK integration for NativeScript

308 lines (270 loc) 9.3 kB
/** * Base Memory Optimizer * Provides common functionality for platform-specific memory optimizers */ import { Logger } from './logger'; /** * Cache interface for anything that can be cleared */ export interface Clearable { clear(): void; } /** * Strategy for memory cleanup */ export enum MemoryCleanupStrategy { LIGHT = 'light', // Clear only non-essential caches MODERATE = 'moderate', // Clear all caches except current view AGGRESSIVE = 'aggressive' // Clear everything, including current view cache } /** * Memory condition levels */ export enum MemoryCondition { NORMAL = 'normal', LOW = 'low', CRITICAL = 'critical' } /** * Device memory profile */ export enum DeviceMemoryProfile { LOW_END = 'low-end', // Devices with <= 2GB RAM MID_RANGE = 'mid-range', // Devices with 3-4GB RAM HIGH_END = 'high-end' // Devices with >= 6GB RAM } /** * Memory threshold configuration */ export interface MemoryThresholds { lowMemoryThresholdMB: number; criticalMemoryThresholdMB: number; } /** * Base class for memory optimizers */ export abstract class MemoryOptimizerBase { protected readonly lowMemoryThresholdMB: number; protected readonly criticalMemoryThresholdMB: number; protected deviceProfile: DeviceMemoryProfile; private memoryCondition: MemoryCondition = MemoryCondition.NORMAL; private caches: Map<string, Clearable> = new Map(); private lastCleanupTime = 0; private cleanupThrottleMs = 5000; // Prevent cleanup more often than every 5 seconds constructor(lowMemoryThresholdMB = 100, criticalMemoryThresholdMB = 50) { // Determine device profile and adjust thresholds accordingly this.deviceProfile = this.detectDeviceProfile(); const adjustedThresholds = this.getAdaptiveThresholds( lowMemoryThresholdMB, criticalMemoryThresholdMB ); this.lowMemoryThresholdMB = adjustedThresholds.lowMemoryThresholdMB; this.criticalMemoryThresholdMB = adjustedThresholds.criticalMemoryThresholdMB; Logger.info( `[MemoryOptimizerBase] Initialized for ${this.deviceProfile} device with` + ` low threshold: ${this.lowMemoryThresholdMB}MB,` + ` critical threshold: ${this.criticalMemoryThresholdMB}MB` ); // Start platform-specific monitoring this.startMemoryMonitoring(); // Start periodic checks this.startPeriodicChecks(); } /** * Check if memory is low */ public isMemoryLow(): boolean { return this.memoryCondition !== MemoryCondition.NORMAL; } /** * Check if memory is critically low */ public isMemoryCritical(): boolean { return this.memoryCondition === MemoryCondition.CRITICAL; } /** * Set the current memory condition */ protected setMemoryCondition(condition: MemoryCondition): void { if (this.memoryCondition !== condition) { this.memoryCondition = condition; switch (condition) { case MemoryCondition.CRITICAL: Logger.warn('[MemoryOptimizerBase] CRITICAL memory condition detected'); this.releaseMemory(MemoryCleanupStrategy.AGGRESSIVE); break; case MemoryCondition.LOW: Logger.info('[MemoryOptimizerBase] LOW memory condition detected'); this.releaseMemory(MemoryCleanupStrategy.MODERATE); break; case MemoryCondition.NORMAL: Logger.debug('[MemoryOptimizerBase] Memory condition returned to NORMAL'); break; } } } /** * Register a cache that can be cleared * @param name Unique name for the cache * @param cache The cache object * @param priority Priority level for when this cache should be cleared (1-10, higher = cleared earlier) */ public registerCache(name: string, cache: Clearable, priority = 5): void { this.caches.set(`${priority.toString().padStart(2, '0')}_${name}`, cache); Logger.debug(`[MemoryOptimizerBase] Registered cache: ${name} (priority: ${priority})`); } /** * Unregister a cache */ public unregisterCache(name: string, priority = 5): void { this.caches.delete(`${priority.toString().padStart(2, '0')}_${name}`); } /** * Start platform-specific memory monitoring */ protected abstract startMemoryMonitoring(): void; /** * Check current memory status * To be implemented by platform-specific classes */ protected abstract checkMemoryStatus(): MemoryCondition; /** * Start periodic memory checks */ private startPeriodicChecks(): void { // Check memory status every 30 seconds setInterval(() => { const condition = this.checkMemoryStatus(); this.setMemoryCondition(condition); }, 30000); } /** * Release memory based on strategy */ protected releaseMemory(strategy: MemoryCleanupStrategy): void { // Throttle cleanup operations const now = Date.now(); if (now - this.lastCleanupTime < this.cleanupThrottleMs) { Logger.debug(`[MemoryOptimizerBase] Skipping cleanup - throttled (last: ${now - this.lastCleanupTime}ms ago)`); return; } this.lastCleanupTime = now; Logger.info(`[MemoryOptimizerBase] Releasing memory with strategy: ${strategy}`); // Sort cache keys by priority const sortedCacheKeys = Array.from(this.caches.keys()).sort(); // Clear caches based on strategy let clearedCount = 0; switch (strategy) { case MemoryCleanupStrategy.AGGRESSIVE: // Clear all caches for (const key of sortedCacheKeys) { const cache = this.caches.get(key); if (cache) { cache.clear(); clearedCount++; } } // Force garbage collection this.forceGarbageCollection(); break; case MemoryCleanupStrategy.MODERATE: // Clear higher priority caches (1-7) for (const key of sortedCacheKeys) { if (key.startsWith('0') || key.startsWith('1') || key.startsWith('2') || key.startsWith('3') || key.startsWith('4') || key.startsWith('5') || key.startsWith('6') || key.startsWith('7')) { const cache = this.caches.get(key); if (cache) { cache.clear(); clearedCount++; } } } break; case MemoryCleanupStrategy.LIGHT: // Clear only high priority caches (1-3) for (const key of sortedCacheKeys) { if (key.startsWith('0') || key.startsWith('1') || key.startsWith('2') || key.startsWith('3')) { const cache = this.caches.get(key); if (cache) { cache.clear(); clearedCount++; } } } break; } Logger.info(`[MemoryOptimizerBase] Released ${clearedCount} caches`); } /** * Platform-specific garbage collection */ protected abstract forceGarbageCollection(): void; /** * Clean up resources */ public cleanup(): void { this.caches.clear(); Logger.info('[MemoryOptimizerBase] Cleaned up memory optimizer'); } /** * Detect the device memory profile based on available RAM * @returns The device memory profile */ protected detectDeviceProfile(): DeviceMemoryProfile { try { const totalMemoryMB = this.getTotalDeviceMemoryMB(); if (totalMemoryMB <= 2048) { return DeviceMemoryProfile.LOW_END; } else if (totalMemoryMB <= 4096) { return DeviceMemoryProfile.MID_RANGE; } else { return DeviceMemoryProfile.HIGH_END; } } catch (error) { Logger.warn('[MemoryOptimizerBase] Error detecting device profile, defaulting to mid-range'); Logger.error('[MemoryOptimizerBase] Detection error details', error); return DeviceMemoryProfile.MID_RANGE; } } /** * Get adaptive memory thresholds based on device profile * @param baseLowThresholdMB - Base low memory threshold in MB * @param baseCriticalThresholdMB - Base critical memory threshold in MB * @returns Adjusted thresholds for the device */ protected getAdaptiveThresholds( baseLowThresholdMB: number, baseCriticalThresholdMB: number ): MemoryThresholds { switch (this.deviceProfile) { case DeviceMemoryProfile.LOW_END: // More aggressive thresholds for low-end devices return { lowMemoryThresholdMB: Math.max(50, baseLowThresholdMB * 0.5), criticalMemoryThresholdMB: Math.max(25, baseCriticalThresholdMB * 0.5) }; case DeviceMemoryProfile.HIGH_END: // More relaxed thresholds for high-end devices return { lowMemoryThresholdMB: baseLowThresholdMB * 2, criticalMemoryThresholdMB: baseCriticalThresholdMB * 2 }; case DeviceMemoryProfile.MID_RANGE: default: // Use base thresholds for mid-range return { lowMemoryThresholdMB: baseLowThresholdMB, criticalMemoryThresholdMB: baseCriticalThresholdMB }; } } /** * Get total device memory in MB * This is a platform-specific implementation * @returns The total device memory in MB */ protected abstract getTotalDeviceMemoryMB(): number; }