393 lines
12 KiB
JavaScript
393 lines
12 KiB
JavaScript
// memory-manager.js - 全局内存管理模块
|
||
|
||
// 全局内存管理对象
|
||
const memoryManager = {
|
||
// 缓存管理
|
||
caches: {
|
||
ipGeolocation: {
|
||
data: new Map(),
|
||
maxSize: 1000,
|
||
order: []
|
||
},
|
||
domainInfo: {
|
||
data: new Map(),
|
||
maxSize: 500
|
||
},
|
||
apiResponses: {
|
||
data: new Map(),
|
||
maxSize: 100,
|
||
ttl: 60000 // 1分钟过期
|
||
}
|
||
},
|
||
|
||
// 资源管理
|
||
resources: {
|
||
timers: [],
|
||
eventListeners: [],
|
||
webSockets: [],
|
||
intervals: []
|
||
},
|
||
|
||
// 内存监控
|
||
monitoring: {
|
||
enabled: true,
|
||
history: [],
|
||
maxHistory: 50,
|
||
threshold: 80 // 内存使用阈值(%)
|
||
},
|
||
|
||
// 初始化
|
||
init() {
|
||
console.log('内存管理器初始化');
|
||
this.startMonitoring();
|
||
this.setupGlobalListeners();
|
||
},
|
||
|
||
// 启动内存监控
|
||
startMonitoring() {
|
||
if (this.monitoring.enabled && performance && performance.memory) {
|
||
setInterval(() => {
|
||
this.checkMemoryUsage();
|
||
}, 30000); // 每30秒检查一次
|
||
}
|
||
},
|
||
|
||
// 检查内存使用情况
|
||
checkMemoryUsage() {
|
||
if (!performance || !performance.memory) return;
|
||
|
||
const memory = performance.memory;
|
||
const usage = {
|
||
timestamp: Date.now(),
|
||
used: Math.round(memory.usedJSHeapSize / 1024 / 1024 * 100) / 100, // MB
|
||
total: Math.round(memory.totalJSHeapSize / 1024 / 1024 * 100) / 100, // MB
|
||
limit: Math.round(memory.jsHeapSizeLimit / 1024 / 1024 * 100) / 100, // MB
|
||
usagePercent: Math.round((memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100 * 100) / 100 // %
|
||
};
|
||
|
||
this.monitoring.history.push(usage);
|
||
|
||
// 限制历史记录大小
|
||
if (this.monitoring.history.length > this.monitoring.maxHistory) {
|
||
this.monitoring.history.shift();
|
||
}
|
||
|
||
// 内存使用过高时的处理
|
||
if (usage.usagePercent > this.monitoring.threshold) {
|
||
console.warn('内存使用过高:', usage);
|
||
this.triggerMemoryCleanup();
|
||
}
|
||
|
||
console.log('内存使用情况:', usage);
|
||
},
|
||
|
||
// 触发内存清理
|
||
triggerMemoryCleanup() {
|
||
console.log('触发内存清理...');
|
||
|
||
// 清理缓存
|
||
this.cleanupCaches();
|
||
|
||
// 清理未使用的资源
|
||
this.cleanupUnusedResources();
|
||
},
|
||
|
||
// 清理缓存
|
||
cleanupCaches() {
|
||
// 清理IP地理位置缓存
|
||
this.cleanupCache('ipGeolocation');
|
||
|
||
// 清理域名信息缓存
|
||
this.cleanupCache('domainInfo');
|
||
|
||
// 清理API响应缓存
|
||
this.cleanupCache('apiResponses');
|
||
},
|
||
|
||
// 清理特定缓存
|
||
cleanupCache(cacheName) {
|
||
const cache = this.caches[cacheName];
|
||
if (!cache) return;
|
||
|
||
console.log(`清理${cacheName}缓存 - 当前大小: ${cache.data.size}`);
|
||
|
||
// 清理超出大小限制的缓存
|
||
if (cache.data.size > cache.maxSize) {
|
||
if (cache.order && cache.order.length > 0) {
|
||
// 使用LRU策略
|
||
while (cache.data.size > cache.maxSize && cache.order.length > 0) {
|
||
const oldestKey = cache.order.shift();
|
||
if (oldestKey) {
|
||
cache.data.delete(oldestKey);
|
||
}
|
||
}
|
||
} else {
|
||
// 简单清理(适用于有TTL的缓存)
|
||
const now = Date.now();
|
||
for (const [key, value] of cache.data.entries()) {
|
||
if (cache.data.size <= cache.maxSize) break;
|
||
|
||
// 检查TTL
|
||
if (cache.ttl && value.timestamp && (now - value.timestamp) > cache.ttl) {
|
||
cache.data.delete(key);
|
||
}
|
||
}
|
||
|
||
// 如果仍然超出大小限制,删除最旧的项
|
||
if (cache.data.size > cache.maxSize) {
|
||
const keys = Array.from(cache.data.keys());
|
||
while (cache.data.size > cache.maxSize && keys.length > 0) {
|
||
const oldestKey = keys.shift();
|
||
cache.data.delete(oldestKey);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log(`清理后${cacheName}缓存大小: ${cache.data.size}`);
|
||
},
|
||
|
||
// 清理未使用的资源
|
||
cleanupUnusedResources() {
|
||
// 清理定时器(这里主要是记录,实际清理需要在具体组件中进行)
|
||
console.log(`当前活动定时器数量: ${this.resources.timers.length}`);
|
||
console.log(`当前活动事件监听器数量: ${this.resources.eventListeners.length}`);
|
||
console.log(`当前活动WebSocket连接数量: ${this.resources.webSockets.length}`);
|
||
console.log(`当前活动间隔定时器数量: ${this.resources.intervals.length}`);
|
||
},
|
||
|
||
// 添加缓存项
|
||
addCacheItem(cacheName, key, value) {
|
||
const cache = this.caches[cacheName];
|
||
if (!cache) return;
|
||
|
||
// 检查缓存大小
|
||
if (cache.data.size >= cache.maxSize) {
|
||
this.cleanupCache(cacheName);
|
||
}
|
||
|
||
// 添加到缓存
|
||
if (cache.ttl) {
|
||
cache.data.set(key, {
|
||
value,
|
||
timestamp: Date.now()
|
||
});
|
||
} else {
|
||
cache.data.set(key, value);
|
||
}
|
||
|
||
// 更新访问顺序(用于LRU)
|
||
if (cache.order) {
|
||
// 移除现有的位置
|
||
const index = cache.order.indexOf(key);
|
||
if (index > -1) {
|
||
cache.order.splice(index, 1);
|
||
}
|
||
// 添加到末尾
|
||
cache.order.push(key);
|
||
}
|
||
},
|
||
|
||
// 获取缓存项
|
||
getCacheItem(cacheName, key) {
|
||
const cache = this.caches[cacheName];
|
||
if (!cache) return null;
|
||
|
||
const item = cache.data.get(key);
|
||
if (!item) return null;
|
||
|
||
// 检查TTL
|
||
if (cache.ttl && item.timestamp && (Date.now() - item.timestamp) > cache.ttl) {
|
||
cache.data.delete(key);
|
||
return null;
|
||
}
|
||
|
||
// 更新访问顺序(用于LRU)
|
||
if (cache.order) {
|
||
// 移除现有的位置
|
||
const index = cache.order.indexOf(key);
|
||
if (index > -1) {
|
||
cache.order.splice(index, 1);
|
||
}
|
||
// 添加到末尾
|
||
cache.order.push(key);
|
||
}
|
||
|
||
return cache.ttl ? item.value : item;
|
||
},
|
||
|
||
// 注册定时器
|
||
registerTimer(timerId) {
|
||
if (timerId) {
|
||
this.resources.timers.push(timerId);
|
||
}
|
||
},
|
||
|
||
// 注销定时器
|
||
unregisterTimer(timerId) {
|
||
const index = this.resources.timers.indexOf(timerId);
|
||
if (index > -1) {
|
||
clearTimeout(timerId);
|
||
this.resources.timers.splice(index, 1);
|
||
}
|
||
},
|
||
|
||
// 注册事件监听器
|
||
registerEventListener(element, event, handler) {
|
||
if (element && event && handler) {
|
||
this.resources.eventListeners.push({ element, event, handler });
|
||
}
|
||
},
|
||
|
||
// 注销事件监听器
|
||
unregisterEventListener(element, event, handler) {
|
||
const index = this.resources.eventListeners.findIndex(item =>
|
||
item.element === element && item.event === event && item.handler === handler
|
||
);
|
||
if (index > -1) {
|
||
element.removeEventListener(event, handler);
|
||
this.resources.eventListeners.splice(index, 1);
|
||
}
|
||
},
|
||
|
||
// 注册WebSocket连接
|
||
registerWebSocket(ws) {
|
||
if (ws) {
|
||
this.resources.webSockets.push(ws);
|
||
}
|
||
},
|
||
|
||
// 注销WebSocket连接
|
||
unregisterWebSocket(ws) {
|
||
const index = this.resources.webSockets.indexOf(ws);
|
||
if (index > -1) {
|
||
try {
|
||
ws.close();
|
||
} catch (error) {
|
||
console.error('关闭WebSocket连接失败:', error);
|
||
}
|
||
this.resources.webSockets.splice(index, 1);
|
||
}
|
||
},
|
||
|
||
// 注册间隔定时器
|
||
registerInterval(intervalId) {
|
||
if (intervalId) {
|
||
this.resources.intervals.push(intervalId);
|
||
}
|
||
},
|
||
|
||
// 注销间隔定时器
|
||
unregisterInterval(intervalId) {
|
||
const index = this.resources.intervals.indexOf(intervalId);
|
||
if (index > -1) {
|
||
clearInterval(intervalId);
|
||
this.resources.intervals.splice(index, 1);
|
||
}
|
||
},
|
||
|
||
// 清理所有资源
|
||
cleanupAllResources() {
|
||
console.log('清理所有资源...');
|
||
|
||
// 清理定时器
|
||
this.resources.timers.forEach(timerId => {
|
||
clearTimeout(timerId);
|
||
});
|
||
this.resources.timers = [];
|
||
|
||
// 清理事件监听器
|
||
this.resources.eventListeners.forEach(({ element, event, handler }) => {
|
||
try {
|
||
element.removeEventListener(event, handler);
|
||
} catch (error) {
|
||
console.error('移除事件监听器失败:', error);
|
||
}
|
||
});
|
||
this.resources.eventListeners = [];
|
||
|
||
// 清理WebSocket连接
|
||
this.resources.webSockets.forEach(ws => {
|
||
try {
|
||
ws.close();
|
||
} catch (error) {
|
||
console.error('关闭WebSocket连接失败:', error);
|
||
}
|
||
});
|
||
this.resources.webSockets = [];
|
||
|
||
// 清理间隔定时器
|
||
this.resources.intervals.forEach(intervalId => {
|
||
clearInterval(intervalId);
|
||
});
|
||
this.resources.intervals = [];
|
||
|
||
// 清理缓存
|
||
this.cleanupCaches();
|
||
|
||
console.log('所有资源已清理');
|
||
},
|
||
|
||
// 设置全局监听器
|
||
setupGlobalListeners() {
|
||
// 页面卸载时清理所有资源
|
||
window.addEventListener('beforeunload', () => {
|
||
this.cleanupAllResources();
|
||
});
|
||
|
||
// 页面可见性变化时的处理
|
||
document.addEventListener('visibilitychange', () => {
|
||
if (document.hidden) {
|
||
// 页面隐藏时清理一些资源
|
||
console.log('页面隐藏,清理资源...');
|
||
this.cleanupCaches();
|
||
}
|
||
});
|
||
},
|
||
|
||
// 获取内存使用统计
|
||
getStats() {
|
||
if (this.monitoring.history.length === 0) {
|
||
return null;
|
||
}
|
||
|
||
const recent = this.monitoring.history[this.monitoring.history.length - 1];
|
||
const avg = this.monitoring.history.reduce((sum, item) => sum + item.used, 0) / this.monitoring.history.length;
|
||
const max = Math.max(...this.monitoring.history.map(item => item.used));
|
||
const min = Math.min(...this.monitoring.history.map(item => item.used));
|
||
|
||
return {
|
||
recent,
|
||
avg: Math.round(avg * 100) / 100,
|
||
max: Math.round(max * 100) / 100,
|
||
min: Math.round(min * 100) / 100,
|
||
history: this.monitoring.history,
|
||
caches: {
|
||
ipGeolocation: this.caches.ipGeolocation.data.size,
|
||
domainInfo: this.caches.domainInfo.data.size,
|
||
apiResponses: this.caches.apiResponses.data.size
|
||
},
|
||
resources: {
|
||
timers: this.resources.timers.length,
|
||
eventListeners: this.resources.eventListeners.length,
|
||
webSockets: this.resources.webSockets.length,
|
||
intervals: this.resources.intervals.length
|
||
}
|
||
};
|
||
}
|
||
};
|
||
|
||
// 导出内存管理器
|
||
if (typeof module !== 'undefined' && module.exports) {
|
||
module.exports = memoryManager;
|
||
} else {
|
||
window.memoryManager = memoryManager;
|
||
}
|
||
|
||
// 自动初始化
|
||
if (typeof window !== 'undefined') {
|
||
window.addEventListener('DOMContentLoaded', () => {
|
||
memoryManager.init();
|
||
});
|
||
}
|