From d33f3434752c672c860acad1e6d41258556483b2 Mon Sep 17 00:00:00 2001 From: nanhaoluo <3075912108@qq.com> Date: Wed, 21 Jan 2026 23:29:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20ArgonMemoryManager?= =?UTF-8?q?=20=E5=86=85=E5=AD=98=E7=AE=A1=E7=90=86=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现构造函数和 ID 跟踪集合(timers, intervals, frames) - 实现 setTimeout()、setInterval()、requestAnimationFrame() 包装方法 - 实现对应的清理方法(clearTimeout, clearInterval, cancelAnimationFrame) - 实现 clearAll() 统一清理接口 - 实现 getStats() 获取统计信息 - 创建完整的 HTML 测试文件验证功能 - 支持参数传递给回调函数 - 自动跟踪和清理,避免内存泄漏 验证需求: - 需求 12.5: 闭包引用大型对象时仅保存必要属性 - 需求 13.4: 组件销毁或页面切换时取消所有定时器和动画帧 --- .../specs/resource-cpu-optimization/tasks.md | 2 +- argon-memory-manager.test.html | 564 ++++++++++++++++++ argon-memory-manager.test.js | 216 +++++++ argon-performance.js | 171 ++++++ 4 files changed, 952 insertions(+), 1 deletion(-) create mode 100644 argon-memory-manager.test.html create mode 100644 argon-memory-manager.test.js diff --git a/.kiro/specs/resource-cpu-optimization/tasks.md b/.kiro/specs/resource-cpu-optimization/tasks.md index 89ffbc3..de4eed5 100644 --- a/.kiro/specs/resource-cpu-optimization/tasks.md +++ b/.kiro/specs/resource-cpu-optimization/tasks.md @@ -101,7 +101,7 @@ - **验证:需求 5.2, 5.3, 5.5** - [~] 7. 实现内存管理模块 - - [~] 7.1 创建 ArgonMemoryManager 类 + - [x] 7.1 创建 ArgonMemoryManager 类 - 实现构造函数和 ID 跟踪集合 - 实现 setTimeout()、setInterval()、requestAnimationFrame() 包装方法 - 实现对应的清理方法 diff --git a/argon-memory-manager.test.html b/argon-memory-manager.test.html new file mode 100644 index 0000000..924f529 --- /dev/null +++ b/argon-memory-manager.test.html @@ -0,0 +1,564 @@ + + + + + + ArgonMemoryManager 测试 + + + +

🧠 ArgonMemoryManager 内存管理模块测试

+ + +
+

1. setTimeout 跟踪测试

+ +
+
+ + +
+

2. setInterval 跟踪测试

+ +
+
+ + +
+

3. requestAnimationFrame 跟踪测试

+ +
+
+ + +
+

4. clearTimeout 测试

+ +
+
+ + +
+

5. clearAll 统一清理测试

+ +
+
+ + +
+

6. 参数传递测试

+ +
+
+ + +
+

7. getStats 统计信息测试

+ +
+
+ + +
+

8. 综合测试

+ +
+
+ + + + + + + diff --git a/argon-memory-manager.test.js b/argon-memory-manager.test.js new file mode 100644 index 0000000..466bc9f --- /dev/null +++ b/argon-memory-manager.test.js @@ -0,0 +1,216 @@ +/** + * ArgonMemoryManager 单元测试 + * 测试内存管理模块的定时器和动画帧跟踪功能 + */ + +// 模拟浏览器环境 +if (typeof window === 'undefined') { + global.window = global; + global.setTimeout = setTimeout; + global.clearTimeout = clearTimeout; + global.setInterval = setInterval; + global.clearInterval = clearInterval; + global.requestAnimationFrame = (callback) => setTimeout(callback, 16); + global.cancelAnimationFrame = clearTimeout; +} + +// 加载模块 +require('./argon-performance.js'); + +const ArgonMemoryManager = window.ArgonMemoryManager; + +describe('ArgonMemoryManager', () => { + let memoryManager; + + beforeEach(() => { + memoryManager = new ArgonMemoryManager(); + }); + + afterEach(() => { + // 清理所有定时器 + memoryManager.clearAll(); + }); + + // Feature: resource-cpu-optimization, Example: setTimeout 跟踪 + test('should track setTimeout and auto-remove after execution', (done) => { + let executed = false; + + const id = memoryManager.setTimeout(() => { + executed = true; + }, 10); + + // 验证 ID 被跟踪 + expect(memoryManager.timers.has(id)).toBe(true); + expect(memoryManager.getStats().timers).toBe(1); + + // 等待执行完成 + setTimeout(() => { + expect(executed).toBe(true); + // 验证执行后自动移除 + expect(memoryManager.timers.has(id)).toBe(false); + expect(memoryManager.getStats().timers).toBe(0); + done(); + }, 50); + }); + + // Feature: resource-cpu-optimization, Example: setInterval 跟踪 + test('should track setInterval', (done) => { + let count = 0; + + const id = memoryManager.setInterval(() => { + count++; + }, 10); + + // 验证 ID 被跟踪 + expect(memoryManager.intervals.has(id)).toBe(true); + expect(memoryManager.getStats().intervals).toBe(1); + + // 等待执行几次 + setTimeout(() => { + expect(count).toBeGreaterThan(0); + // 手动清除 + memoryManager.clearInterval(id); + expect(memoryManager.intervals.has(id)).toBe(false); + expect(memoryManager.getStats().intervals).toBe(0); + done(); + }, 50); + }); + + // Feature: resource-cpu-optimization, Example: requestAnimationFrame 跟踪 + test('should track requestAnimationFrame and auto-remove after execution', (done) => { + let executed = false; + + const id = memoryManager.requestAnimationFrame(() => { + executed = true; + }); + + // 验证 ID 被跟踪 + expect(memoryManager.frames.has(id)).toBe(true); + expect(memoryManager.getStats().frames).toBe(1); + + // 等待执行完成 + setTimeout(() => { + expect(executed).toBe(true); + // 验证执行后自动移除 + expect(memoryManager.frames.has(id)).toBe(false); + expect(memoryManager.getStats().frames).toBe(0); + done(); + }, 50); + }); + + // Feature: resource-cpu-optimization, Example: clearTimeout 移除跟踪 + test('should remove timer from tracking when cleared', () => { + const id = memoryManager.setTimeout(() => {}, 1000); + + expect(memoryManager.timers.has(id)).toBe(true); + + memoryManager.clearTimeout(id); + + expect(memoryManager.timers.has(id)).toBe(false); + }); + + // Feature: resource-cpu-optimization, Example: cancelAnimationFrame 移除跟踪 + test('should remove frame from tracking when cancelled', () => { + const id = memoryManager.requestAnimationFrame(() => {}); + + expect(memoryManager.frames.has(id)).toBe(true); + + memoryManager.cancelAnimationFrame(id); + + expect(memoryManager.frames.has(id)).toBe(false); + }); + + // Feature: resource-cpu-optimization, Example: clearAll 清理所有定时器 + test('should clear all timers, intervals and frames', (done) => { + // 创建多个定时器 + memoryManager.setTimeout(() => {}, 1000); + memoryManager.setTimeout(() => {}, 2000); + memoryManager.setInterval(() => {}, 100); + memoryManager.setInterval(() => {}, 200); + memoryManager.requestAnimationFrame(() => {}); + memoryManager.requestAnimationFrame(() => {}); + + const stats = memoryManager.getStats(); + expect(stats.timers).toBe(2); + expect(stats.intervals).toBe(2); + expect(stats.frames).toBe(2); + expect(stats.total).toBe(6); + + // 清理所有 + memoryManager.clearAll(); + + const statsAfter = memoryManager.getStats(); + expect(statsAfter.timers).toBe(0); + expect(statsAfter.intervals).toBe(0); + expect(statsAfter.frames).toBe(0); + expect(statsAfter.total).toBe(0); + + done(); + }); + + // Feature: resource-cpu-optimization, Example: 多个定时器跟踪 + test('should track multiple timers independently', () => { + const id1 = memoryManager.setTimeout(() => {}, 1000); + const id2 = memoryManager.setTimeout(() => {}, 2000); + const id3 = memoryManager.setTimeout(() => {}, 3000); + + expect(memoryManager.getStats().timers).toBe(3); + + memoryManager.clearTimeout(id2); + + expect(memoryManager.getStats().timers).toBe(2); + expect(memoryManager.timers.has(id1)).toBe(true); + expect(memoryManager.timers.has(id2)).toBe(false); + expect(memoryManager.timers.has(id3)).toBe(true); + }); + + // Feature: resource-cpu-optimization, Example: 传递参数给回调函数 + test('should pass arguments to setTimeout callback', (done) => { + let receivedArgs = null; + + memoryManager.setTimeout((a, b, c) => { + receivedArgs = [a, b, c]; + }, 10, 'arg1', 'arg2', 'arg3'); + + setTimeout(() => { + expect(receivedArgs).toEqual(['arg1', 'arg2', 'arg3']); + done(); + }, 50); + }); + + // Feature: resource-cpu-optimization, Example: 传递参数给 setInterval 回调函数 + test('should pass arguments to setInterval callback', (done) => { + let receivedArgs = null; + + const id = memoryManager.setInterval((a, b) => { + receivedArgs = [a, b]; + }, 10, 'test1', 'test2'); + + setTimeout(() => { + expect(receivedArgs).toEqual(['test1', 'test2']); + memoryManager.clearInterval(id); + done(); + }, 50); + }); + + // Feature: resource-cpu-optimization, Example: getStats 返回正确的统计信息 + test('should return correct stats', () => { + expect(memoryManager.getStats()).toEqual({ + timers: 0, + intervals: 0, + frames: 0, + total: 0 + }); + + memoryManager.setTimeout(() => {}, 1000); + memoryManager.setInterval(() => {}, 1000); + memoryManager.requestAnimationFrame(() => {}); + + expect(memoryManager.getStats()).toEqual({ + timers: 1, + intervals: 1, + frames: 1, + total: 3 + }); + }); +}); diff --git a/argon-performance.js b/argon-performance.js index e56dce6..7f81d4e 100644 --- a/argon-performance.js +++ b/argon-performance.js @@ -677,6 +677,176 @@ class ArgonRenderOptimizer { } } +// ========================================================================== +// 内存管理模块 +// ========================================================================== + +/** + * 内存管理类 + * 跟踪和管理定时器、间隔定时器和动画帧,确保正确清理避免内存泄漏 + * + * @class ArgonMemoryManager + */ +class ArgonMemoryManager { + /** + * 构造函数 + * 初始化 ID 跟踪集合 + */ + constructor() { + this.timers = new Set(); // setTimeout ID 集合 + this.intervals = new Set(); // setInterval ID 集合 + this.frames = new Set(); // requestAnimationFrame ID 集合 + } + + /** + * 设置定时器并跟踪 + * 包装原生 setTimeout,自动跟踪 timer ID + * + * @param {Function} callback - 回调函数 + * @param {number} delay - 延迟时间(毫秒) + * @param {...*} args - 传递给回调函数的参数 + * @returns {number} timer ID,可用于手动清除 + */ + setTimeout(callback, delay, ...args) { + const id = setTimeout(() => { + // 执行完成后自动从跟踪集合中移除 + this.timers.delete(id); + callback(...args); + }, delay); + + this.timers.add(id); + return id; + } + + /** + * 设置间隔定时器并跟踪 + * 包装原生 setInterval,自动跟踪 interval ID + * + * @param {Function} callback - 回调函数 + * @param {number} interval - 间隔时间(毫秒) + * @param {...*} args - 传递给回调函数的参数 + * @returns {number} interval ID,可用于手动清除 + */ + setInterval(callback, interval, ...args) { + const id = setInterval(() => { + callback(...args); + }, interval); + + this.intervals.add(id); + return id; + } + + /** + * 请求动画帧并跟踪 + * 包装原生 requestAnimationFrame,自动跟踪 frame ID + * + * @param {Function} callback - 回调函数 + * @returns {number} frame ID,可用于手动取消 + */ + requestAnimationFrame(callback) { + const id = requestAnimationFrame((timestamp) => { + // 执行完成后自动从跟踪集合中移除 + this.frames.delete(id); + callback(timestamp); + }); + + this.frames.add(id); + return id; + } + + /** + * 清除定时器 + * 包装原生 clearTimeout,同时从跟踪集合中移除 + * + * @param {number} id - timer ID + * @returns {void} + */ + clearTimeout(id) { + clearTimeout(id); + this.timers.delete(id); + } + + /** + * 清除间隔定时器 + * 包装原生 clearInterval,同时从跟踪集合中移除 + * + * @param {number} id - interval ID + * @returns {void} + */ + clearInterval(id) { + clearInterval(id); + this.intervals.delete(id); + } + + /** + * 取消动画帧 + * 包装原生 cancelAnimationFrame,同时从跟踪集合中移除 + * + * @param {number} id - frame ID + * @returns {void} + */ + cancelAnimationFrame(id) { + cancelAnimationFrame(id); + this.frames.delete(id); + } + + /** + * 清理所有定时器和动画帧 + * 通常在 PJAX 页面切换或组件销毁时调用 + * 确保所有待执行的回调都被取消,避免内存泄漏 + * + * @returns {void} + */ + clearAll() { + // 清除所有 setTimeout + this.timers.forEach(id => { + try { + clearTimeout(id); + } catch (e) { + // 静默失败,ID 可能已失效 + } + }); + + // 清除所有 setInterval + this.intervals.forEach(id => { + try { + clearInterval(id); + } catch (e) { + // 静默失败,ID 可能已失效 + } + }); + + // 取消所有 requestAnimationFrame + this.frames.forEach(id => { + try { + cancelAnimationFrame(id); + } catch (e) { + // 静默失败,ID 可能已失效 + } + }); + + // 清空所有集合 + this.timers.clear(); + this.intervals.clear(); + this.frames.clear(); + } + + /** + * 获取当前跟踪的定时器数量 + * 用于调试和性能监控 + * + * @returns {Object} 包含各类定时器数量的对象 + */ + getStats() { + return { + timers: this.timers.size, + intervals: this.intervals.size, + frames: this.frames.size, + total: this.timers.size + this.intervals.size + this.frames.size + }; + } +} + // ========================================================================== // 模块导出和初始化接口 // ========================================================================== @@ -702,5 +872,6 @@ if (typeof window !== 'undefined') { window.ArgonEventManager = ArgonEventManager; window.ArgonResourceLoader = ArgonResourceLoader; window.ArgonRenderOptimizer = ArgonRenderOptimizer; + window.ArgonMemoryManager = ArgonMemoryManager; window.initArgonPerformance = initArgonPerformance; }