ECS 缓存管理优化

This commit is contained in:
dgflash
2026-03-08 11:49:53 +08:00
parent 47c4e935f9
commit 2350b06c84
7 changed files with 1384 additions and 46 deletions

View File

@@ -5,6 +5,7 @@ import { ECSMatcher } from './ECSMatcher';
import type { CompCtor, CompType, EntityCtor } from './ECSModel';
import { ECSModel } from './ECSModel';
import { ECSComblockSystem, ECSRootSystem, ECSSystem } from './ECSSystem';
import { globalPoolCoordinator } from './ECSPoolManager';
/**
* ECSEntity对象在destroy后会回收到ECSModel.entityPool实体对象池中
@@ -168,7 +169,7 @@ export namespace ecs {
}
/**
* 创建一个新的实体对象或从缓存中获取一个实体对象
* 创建一个新的实体对象或从缓存中获取一个实体对象(使用动态池)
* @param ctor 实体类
*/
export function getEntity<T extends Entity>(ctor: EntityCtor<T>): T {
@@ -179,16 +180,19 @@ export namespace ecs {
throw new Error(`${ctor.name} 实体没有注册`);
}
// 获取实体对象池
const entitys = ECSModel.entityPool.get(entityName) || [];
let entity: T | undefined = entitys.pop() as T | undefined;
// 使用动态池管理器
const pool = globalPoolCoordinator.getPool(
entityName,
() => {
const entity = new ctor();
entity.eid = ECSModel.eid++; // 实体唯一编号
entity.name = entityName;
return entity;
},
ctor
);
// 缓存中没有同类实体,则创建一个新的
if (!entity) {
entity = new ctor();
entity.eid = ECSModel.eid++; // 实体唯一编号
entity.name = entityName;
}
const entity = pool.get();
// 触发实体初始化逻辑
const entityWithInit = entity as ECSEntity & { init?: () => void };
@@ -227,6 +231,9 @@ export namespace ecs {
});
ECSModel.eid2Entity.clear();
ECSModel.groups.clear();
// 保存历史数据用于下次学习
globalPoolCoordinator.saveHistory();
}
/**
@@ -234,19 +241,22 @@ export namespace ecs {
* 注意:此操作会清空所有实体池、组件池和 Mask 池,请在确保不再需要这些缓存时调用
*/
export function clearPools(): void {
// 清理实体池
// 清理旧的实体池
ECSModel.entityPool.forEach((pool) => {
pool.length = 0;
});
ECSModel.entityPool.clear();
// 清理组件池
// 清理旧的组件池
ECSModel.compPools.forEach((pool) => {
pool.length = 0;
});
// 清理 Mask 对象池
ECSMask.clearPool();
// 清理动态池管理器
globalPoolCoordinator.getSmartManager().clearAll();
}
/**

View File

@@ -2,6 +2,7 @@ import type { ecs } from './ECS';
import { ECSMask } from './ECSMask';
import type { CompCtor, CompType } from './ECSModel';
import { ECSModel } from './ECSModel';
import { globalPoolCoordinator } from './ECSPoolManager';
//#region 辅助方法
@@ -45,7 +46,7 @@ function deleteEntityComp(entity: ECSEntity, compName: string): void {
}
/**
* 创建组件对象
* 创建组件对象(使用动态池管理)
* @param ctor
*/
function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
@@ -53,37 +54,36 @@ function createComp<T extends ecs.IComp>(ctor: CompCtor<T>): T {
if (!cct) {
throw Error(`没有找到该组件的构造函数,检查${ctor.compName}是否为不可构造的组件`);
}
const comps = ECSModel.compPools.get(ctor.tid);
if (!comps) {
throw Error(`组件${ctor.compName}的对象池不存在`);
}
const component = comps.pop() || new (cct as CompCtor<T>)();
// 使用动态池管理器
const pool = globalPoolCoordinator.getPool(
ctor.compName,
() => new (cct as CompCtor<T>)(),
ctor
);
const component = pool.get();
return component as T;
}
/**
* 销毁实体
* 销毁实体(使用动态池管理)
*
* 缓存销毁的实体,下次新建实体时会优先从缓存中拿
* @param entity
*/
function destroyEntity(entity: ECSEntity): void {
if (ECSModel.eid2Entity.has(entity.eid)) {
let entitys = ECSModel.entityPool.get(entity.name);
if (entitys === undefined) {
entitys = [];
ECSModel.entityPool.set(entity.name, entitys);
}
// 限制对象池大小,防止内存无限增长
if (entitys.length < ECSModel.MAX_ENTITY_POOL_SIZE) {
// 池未满:保留 Uint32Array仅清空位图复用时零开销
entity.getMask().clear();
entitys.push(entity);
}
else {
// 池已满:实体真正丢弃,将 Uint32Array 回收到 MaskPool 供后续复用
entity.getMask().destroy();
}
// 使用动态池管理器
const pool = globalPoolCoordinator.getPool(
entity.name,
() => new ECSEntity()
);
// 清空mask并回收
entity.getMask().clear();
pool.recycle(entity);
ECSModel.eid2Entity.delete(entity.eid);
}
else {
@@ -133,7 +133,7 @@ export class ECSEntity {
}
/**
* 添加子实体
* 添加子实体(带循环引用检测)
* @param entity 被添加的实体对象
* @returns 子实体的唯一编号, -1表示添加失败
*/
@@ -147,11 +147,29 @@ export class ECSEntity {
return -1;
}
// 检测循环引用
if (this.hasAncestor(entity)) {
console.error(`检测到循环引用: ${this.name} -> ${entity.name}`);
return -1;
}
entity._parent = this;
this.childs.set(entity.eid, entity);
return entity.eid;
}
/**
* 检测是否存在祖先关系(防止循环引用)
*/
private hasAncestor(entity: ECSEntity): boolean {
let current: ECSEntity | null = this;
while (current) {
if (current === entity) return true;
current = current.parent;
}
return false;
}
/**
* 移除子实体
* @param entity 被移除的实体对象
@@ -290,11 +308,16 @@ export class ECSEntity {
if (isRecycle) {
comp.reset();
// 回收组件到指定缓存池中,限制池大小
// 使用动态池管理器回收组件
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId);
if (compPoolsType && compPoolsType.length < ECSModel.MAX_COMP_POOL_SIZE) {
compPoolsType.push(comp);
const ctor = ECSModel.compCtors[componentTypeId];
if (ctor) {
const pool = globalPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)(),
ctor
);
pool.recycle(comp);
}
}
}
@@ -308,9 +331,14 @@ export class ECSEntity {
console.warn(`实体 ${this.name} 缓存组件数量超过限制,强制回收组件 ${compName}`);
comp.reset();
if (comp.canRecycle) {
const compPoolsType = ECSModel.compPools.get(componentTypeId);
if (compPoolsType && compPoolsType.length < ECSModel.MAX_COMP_POOL_SIZE) {
compPoolsType.push(comp);
const ctor = ECSModel.compCtors[componentTypeId];
if (ctor) {
const pool = globalPoolCoordinator.getPool(
ctor.compName,
() => new (ctor as any)(),
ctor
);
pool.recycle(comp);
}
}
}

View File

@@ -17,19 +17,25 @@ export class ECSGroup<E extends ECSEntity = ECSEntity> {
private _cacheValid = false;
/**
* 符合规则的实体
* 符合规则的实体(带数组容量管理)
*/
get matchEntities(): E[] {
if (!this._cacheValid) {
// 复用数组,减少 GC 压力
const cache = this._entitiesCache;
cache.length = 0;
const targetSize = this._matchEntities.size;
// 如果缓存数组过大,重新创建以释放内存
if (cache.length > targetSize * 2 && targetSize < 100) {
this._entitiesCache = [];
} else {
cache.length = 0;
}
// 直接遍历 Map values 比 Array.from 更高效
const iterator = this._matchEntities.values();
let result = iterator.next();
while (!result.done) {
cache.push(result.value);
this._entitiesCache.push(result.value);
result = iterator.next();
}

View File

@@ -0,0 +1,464 @@
/*
* ECS内存池监控工具
* 用于实时监控和诊断对象池的使用情况
*/
import { globalPoolCoordinator } from './ECSPoolManager';
/** 内存监控统计 */
export interface MemoryStats {
poolName: string;
currentSize: number;
maxSize: number;
minSize: number;
hitRate: number;
createCount: number;
recycleCount: number;
createFrequency: number;
accessFrequency: number;
estimatedMemory: string;
memoryBytes: number;
hasIssue: boolean;
issueType?: 'low-hit-rate' | 'memory-leak' | 'over-capacity' | 'unused';
}
/** 监控配置 */
export interface MonitorConfig {
/** 详细程度compact(简洁) | normal(普通) | detailed(详细) */
level?: 'compact' | 'normal' | 'detailed';
/** 是否显示异常池 */
showIssues?: boolean;
/** 是否显示内存信息 */
showMemory?: boolean;
/** 是否按某个字段排序 */
sortBy?: 'frequency' | 'hitRate' | 'memory' | 'name';
}
/** 异常检测阈值 */
interface IssueThresholds {
lowHitRate: number; // 低命中率阈值默认50%
highCapacity: number; // 高容量使用率阈值默认90%
unusedTime: number; // 未使用时间阈值默认60秒
memoryLeakGrowth: number; // 内存泄漏增长率阈值默认200%
}
/**
* ECS内存监控器
*/
export class ECSMemoryMonitor {
private static instance: ECSMemoryMonitor;
private updateInterval: number = 1000; // 1秒更新一次
private isMonitoring = false;
private timerId: any = null;
private lastStats: Map<string, MemoryStats> = new Map();
private thresholds: IssueThresholds = {
lowHitRate: 50,
highCapacity: 90,
unusedTime: 60000,
memoryLeakGrowth: 200
};
private constructor() { }
static getInstance(): ECSMemoryMonitor {
if (!ECSMemoryMonitor.instance) {
ECSMemoryMonitor.instance = new ECSMemoryMonitor();
}
return ECSMemoryMonitor.instance;
}
/** 设置异常检测阈值 */
setThresholds(thresholds: Partial<IssueThresholds>): void {
this.thresholds = { ...this.thresholds, ...thresholds };
}
/** 获取所有池的统计信息 */
getStats(): MemoryStats[] {
const smartManager = globalPoolCoordinator.getSmartManager();
const allMetrics = smartManager.getAllMetrics();
const stats: MemoryStats[] = [];
const now = Date.now();
allMetrics.forEach((metrics, poolName) => {
const totalAccess = metrics.hitCount + metrics.missCount;
const hitRate = totalAccess > 0 ? (metrics.hitCount / totalAccess) * 100 : 0;
// 计算创建频率
let createFrequency = 0;
if (metrics.createTimes.length >= 2) {
const timeSpan = metrics.createTimes[metrics.createTimes.length - 1] -
metrics.createTimes[0];
if (timeSpan > 0) {
createFrequency = (metrics.createTimes.length / timeSpan) * 1000;
}
}
// 计算访问频率
let accessFrequency = 0;
if (metrics.accessTimes && metrics.accessTimes.length >= 2) {
const timeSpan = metrics.accessTimes[metrics.accessTimes.length - 1] -
metrics.accessTimes[0];
if (timeSpan > 0) {
accessFrequency = (metrics.accessTimes.length / timeSpan) * 1000;
}
}
// 检测异常
const issue = this.detectIssue(poolName, metrics, hitRate, now);
const stat: MemoryStats = {
poolName,
currentSize: metrics.currentSize,
maxSize: metrics.maxSize,
minSize: metrics.minSize,
hitRate,
createCount: metrics.createCount,
recycleCount: metrics.recycleCount,
createFrequency,
accessFrequency,
estimatedMemory: this.formatBytes(metrics.totalMemory),
memoryBytes: metrics.totalMemory,
hasIssue: issue !== null,
issueType: issue || undefined
};
stats.push(stat);
this.lastStats.set(poolName, stat);
});
return stats;
}
/** 检测池的异常情况 */
private detectIssue(poolName: string, metrics: any, hitRate: number, now: number):
'low-hit-rate' | 'memory-leak' | 'over-capacity' | 'unused' | null {
// 检测低命中率
if (metrics.hitCount + metrics.missCount > 20 && hitRate < this.thresholds.lowHitRate) {
return 'low-hit-rate';
}
// 检测容量过高
const capacityUsage = metrics.maxSize > 0 ? (metrics.currentSize / metrics.maxSize) * 100 : 0;
if (capacityUsage > this.thresholds.highCapacity) {
return 'over-capacity';
}
// 检测长期未使用
if (now - metrics.lastAccessTime > this.thresholds.unusedTime) {
return 'unused';
}
// 检测内存泄漏(与上次对比)
const lastStat = this.lastStats.get(poolName);
if (lastStat && lastStat.memoryBytes > 0) {
const growthRate = (metrics.totalMemory / lastStat.memoryBytes) * 100;
if (growthRate > this.thresholds.memoryLeakGrowth && metrics.totalMemory > 1024 * 1024) {
return 'memory-leak';
}
}
return null;
}
/** 格式化字节数 */
private formatBytes(bytes: number): string {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
/**
* 统一的日志输出方法
* @param config 监控配置
*/
log(config: MonitorConfig = {}): void {
const {
level = 'normal',
showIssues = true,
showMemory = true,
sortBy = 'frequency'
} = config;
const stats = this.getStats();
if (stats.length === 0) {
console.log('[ECS Pool] 暂无对象池数据');
return;
}
// 排序
this.sortStats(stats, sortBy);
// 根据详细程度输出
switch (level) {
case 'compact':
this.logCompact(stats, showMemory);
break;
case 'detailed':
this.logDetailed(stats, showIssues, showMemory);
break;
default:
this.logNormal(stats, showIssues, showMemory);
}
}
/** 简洁输出 */
private logCompact(stats: MemoryStats[], showMemory: boolean): void {
const totalCurrent = stats.reduce((sum, s) => sum + s.currentSize, 0);
const avgHitRate = stats.reduce((sum, s) => sum + s.hitRate, 0) / stats.length;
const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0);
const issueCount = stats.filter(s => s.hasIssue).length;
let output = `[ECS Pool] 池数:${stats.length} | 缓存:${totalCurrent} | 命中率:${avgHitRate.toFixed(1)}%`;
if (showMemory) {
output += ` | 内存:${this.formatBytes(totalMemory)}`;
}
if (issueCount > 0) {
output += ` | ⚠️异常:${issueCount}`;
}
console.log(output);
}
/** 普通输出 */
private logNormal(stats: MemoryStats[], showIssues: boolean, showMemory: boolean): void {
console.log('\n========== ECS 对象池统计 ==========');
console.log(`总池数量: ${stats.length}`);
if (showMemory) {
const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0);
const memoryUsage = globalPoolCoordinator.getMemoryUsage();
console.log(`总内存: ${this.formatBytes(totalMemory)} / ${this.formatBytes(memoryUsage.budget)} (${memoryUsage.percent.toFixed(1)}%)`);
}
if (showIssues) {
const issueStats = stats.filter(s => s.hasIssue);
if (issueStats.length > 0) {
console.log(`⚠️ 异常池: ${issueStats.length}`);
}
}
console.log('');
stats.forEach(stat => {
const icon = this.getStatusIcon(stat);
console.log(`${icon}${stat.poolName}`);
console.log(` 容量: ${stat.currentSize}/${stat.maxSize} (最小:${stat.minSize})`);
console.log(` 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`);
console.log(` 创建/回收: ${stat.createCount}/${stat.recycleCount}`);
console.log(` 频率: 创建${stat.createFrequency.toFixed(2)}/s, 访问${stat.accessFrequency.toFixed(2)}/s`);
if (showMemory) {
console.log(` 内存: ${stat.estimatedMemory}`);
}
if (showIssues && stat.hasIssue) {
console.log(` ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`);
}
console.log('');
});
console.log('====================================\n');
}
/** 详细输出 */
private logDetailed(stats: MemoryStats[], showIssues: boolean, showMemory: boolean): void {
console.log('ECS 对象池详细统计报告');
// 总览
const totalCreate = stats.reduce((sum, s) => sum + s.createCount, 0);
const totalRecycle = stats.reduce((sum, s) => sum + s.recycleCount, 0);
const totalCurrent = stats.reduce((sum, s) => sum + s.currentSize, 0);
const avgHitRate = stats.reduce((sum, s) => sum + s.hitRate, 0) / stats.length;
const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0);
console.log('📊 总体概况');
console.log('─────────────────────────────────────────────────────────');
console.log(` 对象池数量: ${stats.length}`);
console.log(` 总创建次数: ${totalCreate}`);
console.log(` 总回收次数: ${totalRecycle}`);
console.log(` 当前缓存数: ${totalCurrent}`);
console.log(` 平均命中率: ${avgHitRate.toFixed(1)}%`);
if (showMemory) {
const memoryUsage = globalPoolCoordinator.getMemoryUsage();
console.log(` 总内存占用: ${this.formatBytes(totalMemory)}`);
console.log(` 内存预算: ${this.formatBytes(memoryUsage.budget)}`);
console.log(` 内存使用率: ${memoryUsage.percent.toFixed(1)}%`);
}
console.log('');
// 异常池
if (showIssues) {
const issueStats = stats.filter(s => s.hasIssue);
if (issueStats.length > 0) {
console.log('⚠️ 异常池列表');
console.log('─────────────────────────────────────────────────────────');
issueStats.forEach(stat => {
console.log(` ${stat.poolName}: ${this.getIssueDesc(stat.issueType!)}`);
});
console.log('');
}
}
console.log('📈 各池详细数据');
console.log('─────────────────────────────────────────────────────────\n');
stats.forEach((stat, index) => {
const usage = stat.maxSize > 0 ? (stat.currentSize / stat.maxSize * 100).toFixed(1) : '0';
const icon = this.getStatusIcon(stat);
console.log(`${index + 1}. ${stat.poolName} ${icon}`);
console.log(` ├─ 容量: ${stat.currentSize}/${stat.maxSize} (${usage}% 使用率)`);
console.log(` ├─ 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`);
console.log(` ├─ 创建/回收: ${stat.createCount} / ${stat.recycleCount}`);
console.log(` ├─ 创建频率: ${stat.createFrequency.toFixed(2)} 个/秒`);
console.log(` ├─ 访问频率: ${stat.accessFrequency.toFixed(2)} 个/秒`);
if (showMemory) {
console.log(` ├─ 估算内存: ${stat.estimatedMemory}`);
}
if (showIssues && stat.hasIssue) {
console.log(` └─ ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`);
} else {
console.log(` └─ 状态: 正常`);
}
console.log('');
});
console.log('═════════════════════════════════════════════════════════\n');
}
/** 排序统计数据 */
private sortStats(stats: MemoryStats[], sortBy: string): void {
switch (sortBy) {
case 'frequency':
stats.sort((a, b) => b.createFrequency - a.createFrequency);
break;
case 'hitRate':
stats.sort((a, b) => b.hitRate - a.hitRate);
break;
case 'memory':
stats.sort((a, b) => b.memoryBytes - a.memoryBytes);
break;
case 'name':
stats.sort((a, b) => a.poolName.localeCompare(b.poolName));
break;
}
}
/** 获取状态图标 */
private getStatusIcon(stat: MemoryStats): string {
if (stat.hasIssue) {
switch (stat.issueType) {
case 'low-hit-rate': return '🔴';
case 'memory-leak': return '💥';
case 'over-capacity': return '⚠️';
case 'unused': return '💤';
}
}
return stat.hitRate >= 80 ? '🟢' : stat.hitRate >= 50 ? '🟡' : '🔴';
}
/** 获取异常描述 */
private getIssueDesc(issueType: string): string {
switch (issueType) {
case 'low-hit-rate': return '命中率过低';
case 'memory-leak': return '疑似内存泄漏';
case 'over-capacity': return '容量使用率过高';
case 'unused': return '长期未使用';
default: return '未知异常';
}
}
/** 打印指定池的统计 */
logPool(poolName: string): void {
const stats = this.getStats();
const stat = stats.find(s => s.poolName === poolName);
if (!stat) {
console.log(`[ECS Pool] 未找到池: ${poolName}`);
return;
}
const usage = stat.maxSize > 0 ? (stat.currentSize / stat.maxSize * 100).toFixed(1) : '0';
const icon = this.getStatusIcon(stat);
console.log(`\n【${stat.poolName}${icon}`);
console.log(` 容量使用: ${stat.currentSize}/${stat.maxSize} (${usage}%)`);
console.log(` 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`);
console.log(` 创建次数: ${stat.createCount}`);
console.log(` 回收次数: ${stat.recycleCount}`);
console.log(` 创建频率: ${stat.createFrequency.toFixed(2)} 个/秒`);
console.log(` 访问频率: ${stat.accessFrequency.toFixed(2)} 个/秒`);
console.log(` 估算内存: ${stat.estimatedMemory}`);
if (stat.hasIssue) {
console.log(` ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`);
}
console.log('');
}
/** 获取命中率描述 */
private getHitRateDesc(hitRate: number): string {
if (hitRate >= 90) return '(优秀)';
if (hitRate >= 70) return '(良好)';
if (hitRate >= 50) return '(一般)';
return '(较差)';
}
/** 开始监控(定时打印) */
startMonitoring(config: MonitorConfig & { interval?: number } = {}): void {
if (this.isMonitoring) {
console.warn('[Monitor] 监控已在运行中');
return;
}
const { interval = 5000, ...monitorConfig } = config;
this.updateInterval = interval;
this.isMonitoring = true;
console.log(`[Monitor] 开始监控,更新间隔: ${interval}ms`);
this.timerId = setInterval(() => {
this.log(monitorConfig);
}, interval);
}
/** 停止监控 */
stopMonitoring(): void {
if (!this.isMonitoring) {
return;
}
if (this.timerId) {
clearInterval(this.timerId);
this.timerId = null;
}
this.isMonitoring = false;
console.log('[Monitor] 监控已停止');
}
/** 获取异常池列表 */
getIssues(): MemoryStats[] {
return this.getStats().filter(s => s.hasIssue);
}
/** 获取类型分析报告 */
getTypeProfiles() {
return globalPoolCoordinator.getSmartManager().getTypeProfiles();
}
/** 触发内存清理 */
cleanup(maxAge: number = 60000): number {
return globalPoolCoordinator.getSmartManager().cleanupAllStale(maxAge);
}
}
/** 导出单例 */
export const ecsMonitor = ECSMemoryMonitor.getInstance();

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c1cce79d-eaea-4205-b53c-982ee27bda62",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}

View File

@@ -0,0 +1,808 @@
/*
* 动态对象池管理系统
* 三阶段实现:基础动态池 + 智能特性 + 可选配置
*/
//#region 类型定义
/** 池统计指标 */
interface PoolMetrics {
// 使用统计
createCount: number; // 创建次数
recycleCount: number; // 回收次数
hitCount: number; // 命中次数(从池中获取)
missCount: number; // 未命中次数(需要新建)
// 容量管理
currentSize: number; // 当前池大小
maxSize: number; // 最大容量
minSize: number; // 最小容量
// 时间统计
lastAccessTime: number; // 最后访问时间
lastAdjustTime: number; // 最后调整时间
createTimes: number[]; // 创建时间戳列表(用于计算频率)
// 内存估算
estimatedItemSize: number; // 单个对象估算大小(字节)
totalMemory: number; // 总内存占用
// LRU支持
accessTimes: number[]; // 访问时间戳用于LRU
}
/** 对象类型特征 */
interface TypeProfile {
typeName: string;
category: 'long-lived' | 'high-frequency' | 'normal' | 'rare';
peakRate: number; // 峰值创建速率(个/秒)
reuseRate: number; // 复用率
avgMemorySize: number; // 平均内存大小
shouldPool: boolean; // 是否应该池化
}
/** 池配置 */
export interface PoolConfig {
enabled?: boolean; // 是否启用池化
minSize?: number; // 最小容量
maxSize?: number; // 最大容量
strategy?: 'dynamic' | 'fixed' | 'lru'; // 策略
preWarmSize?: number; // 预热数量
}
/** 池提示(性能优化) */
export interface PoolHint {
expectedPeak?: number; // 预期峰值
sceneAware?: string; // 场景感知标识
highFrequency?: boolean; // 是否高频对象
}
/** 历史数据 */
interface HistoryData {
typeName: string;
peakCount: number;
avgCount: number;
lastPlayTime: number;
}
//#endregion
//#region 第一阶段:基础动态池
/**
* 动态对象池
* 自动统计、自动调整容量、支持LRU策略
*/
class DynamicPool<T> {
private pool: T[] = [];
private metrics: PoolMetrics;
private config: PoolConfig;
private lruTimestamps: Map<T, number> = new Map(); // LRU时间戳
constructor(
private typeName: string,
private factory: () => T,
config: Partial<PoolConfig> = {}
) {
this.config = {
enabled: true,
minSize: 10,
maxSize: 1000,
strategy: 'dynamic',
preWarmSize: 0,
...config
};
this.metrics = {
createCount: 0,
recycleCount: 0,
hitCount: 0,
missCount: 0,
currentSize: 0,
maxSize: this.config.maxSize!,
minSize: this.config.minSize!,
lastAccessTime: Date.now(),
lastAdjustTime: Date.now(),
createTimes: [],
estimatedItemSize: 0,
totalMemory: 0,
accessTimes: []
};
// 预热
if (this.config.preWarmSize! > 0) {
this.preWarm(this.config.preWarmSize!);
}
// 估算对象大小
this.estimateItemSize();
}
/** 估算单个对象的内存大小 */
private estimateItemSize(): void {
if (this.metrics.estimatedItemSize > 0) return;
try {
const sample = this.factory();
this.metrics.estimatedItemSize = this.calculateObjectSize(sample);
this.updateTotalMemory();
} catch (e) {
console.warn(`[Pool] ${this.typeName} 无法估算对象大小`, e);
this.metrics.estimatedItemSize = 1024; // 默认1KB
}
}
/** 计算对象大小(粗略估算) */
private calculateObjectSize(obj: any): number {
let size = 0;
const seen = new WeakSet();
const calculate = (o: any): void => {
if (o === null || o === undefined) return;
if (typeof o !== 'object') {
size += 8; // 基本类型约8字节
return;
}
if (seen.has(o)) return;
seen.add(o);
// 对象本身的开销
size += 32;
// 遍历属性
for (const key in o) {
if (o.hasOwnProperty(key)) {
size += key.length * 2; // 键名
const value = o[key];
if (typeof value === 'string') {
size += value.length * 2;
} else if (typeof value === 'number' || typeof value === 'boolean') {
size += 8;
} else if (typeof value === 'object' && value !== null) {
calculate(value);
}
}
}
};
calculate(obj);
return size;
}
/** 更新总内存占用 */
private updateTotalMemory(): void {
this.metrics.totalMemory = this.metrics.currentSize * this.metrics.estimatedItemSize;
}
/** 获取对象 */
get(): T {
const now = Date.now();
this.metrics.lastAccessTime = now;
this.metrics.accessTimes.push(now);
// 只保留最近100次访问时间
if (this.metrics.accessTimes.length > 100) {
this.metrics.accessTimes.shift();
}
if (this.pool.length > 0) {
this.metrics.hitCount++;
const item = this.pool.pop()!;
// LRU: 记录使用时间
if (this.config.strategy === 'lru') {
this.lruTimestamps.set(item, now);
}
this.metrics.currentSize = this.pool.length;
this.updateTotalMemory();
return item;
}
// 池中没有,创建新对象
this.metrics.missCount++;
this.metrics.createCount++;
this.metrics.createTimes.push(now);
// 只保留最近100次创建时间
if (this.metrics.createTimes.length > 100) {
this.metrics.createTimes.shift();
}
const item = this.factory();
// LRU: 记录创建时间
if (this.config.strategy === 'lru') {
this.lruTimestamps.set(item, now);
}
// 触发自动调整
this.autoAdjust();
return item;
}
/** 回收对象 */
recycle(item: T): void {
if (!this.config.enabled) {
return;
}
this.metrics.recycleCount++;
// 检查是否超过最大容量
if (this.pool.length < this.metrics.maxSize) {
this.pool.push(item);
this.metrics.currentSize = this.pool.length;
this.updateTotalMemory();
} else if (this.config.strategy === 'lru') {
// LRU策略移除最久未使用的对象
this.evictLRU();
this.pool.push(item);
this.metrics.currentSize = this.pool.length;
this.updateTotalMemory();
}
// 触发自动调整
this.autoAdjust();
}
/** LRU淘汰策略 */
private evictLRU(): void {
if (this.pool.length === 0) return;
let oldestItem: T | null = null;
let oldestTime = Date.now();
// 找到最久未使用的对象
for (const item of this.pool) {
const time = this.lruTimestamps.get(item) || 0;
if (time < oldestTime) {
oldestTime = time;
oldestItem = item;
}
}
// 移除最久未使用的对象
if (oldestItem) {
const index = this.pool.indexOf(oldestItem);
if (index > -1) {
this.pool.splice(index, 1);
this.lruTimestamps.delete(oldestItem);
}
}
}
/** 自动调整池容量 */
private autoAdjust(): void {
const now = Date.now();
const timeSinceLastAdjust = now - this.metrics.lastAdjustTime;
// 每5秒调整一次
if (timeSinceLastAdjust < 5000) {
return;
}
this.metrics.lastAdjustTime = now;
if (this.config.strategy === 'fixed') {
return; // 固定策略不调整
}
// 计算命中率
const totalAccess = this.metrics.hitCount + this.metrics.missCount;
if (totalAccess < 10) {
return; // 样本太少,不调整
}
const hitRate = this.metrics.hitCount / totalAccess;
// 根据命中率调整最大容量
if (hitRate < 0.5) {
// 命中率低,需要扩容
const newMax = Math.min(
Math.floor(this.metrics.maxSize * 1.5),
this.config.maxSize!
);
if (newMax > this.metrics.maxSize) {
console.log(`[Pool] ${this.typeName} 扩容: ${this.metrics.maxSize} -> ${newMax} (命中率: ${(hitRate * 100).toFixed(1)}%)`);
this.metrics.maxSize = newMax;
}
} else if (hitRate > 0.9 && this.pool.length > this.metrics.minSize * 2) {
// 命中率高且池很大,可以缩容
const newMax = Math.max(
Math.floor(this.metrics.maxSize * 0.8),
this.metrics.minSize
);
if (newMax < this.metrics.maxSize) {
console.log(`[Pool] ${this.typeName} 缩容: ${this.metrics.maxSize} -> ${newMax} (命中率: ${(hitRate * 100).toFixed(1)}%)`);
this.metrics.maxSize = newMax;
// 清理多余对象
while (this.pool.length > newMax) {
this.pool.pop();
}
this.metrics.currentSize = this.pool.length;
}
}
}
/** 预热池 */
preWarm(count: number): void {
const targetCount = Math.min(count, this.metrics.maxSize);
while (this.pool.length < targetCount) {
this.pool.push(this.factory());
this.metrics.createCount++;
}
this.metrics.currentSize = this.pool.length;
console.log(`[Pool] ${this.typeName} 预热完成: ${this.pool.length} 个对象`);
}
/** 获取统计信息 */
getMetrics(): Readonly<PoolMetrics> {
return { ...this.metrics };
}
/** 清空池 */
clear(): void {
this.pool.length = 0;
this.lruTimestamps.clear();
this.metrics.currentSize = 0;
this.updateTotalMemory();
}
/** 清理长期未使用的对象(用于内存压力下的主动清理) */
cleanupStale(maxAge: number = 60000): number {
if (this.config.strategy !== 'lru') return 0;
const now = Date.now();
let cleaned = 0;
// 保留最小容量
const targetSize = Math.max(this.metrics.minSize, Math.floor(this.pool.length * 0.5));
for (let i = this.pool.length - 1; i >= 0 && this.pool.length > targetSize; i--) {
const item = this.pool[i];
const lastUse = this.lruTimestamps.get(item) || 0;
if (now - lastUse > maxAge) {
this.pool.splice(i, 1);
this.lruTimestamps.delete(item);
cleaned++;
}
}
if (cleaned > 0) {
this.metrics.currentSize = this.pool.length;
this.updateTotalMemory();
console.log(`[Pool] ${this.typeName} 清理了 ${cleaned} 个过期对象`);
}
return cleaned;
}
/** 计算创建频率(个/秒) */
getCreateFrequency(): number {
if (this.metrics.createTimes.length < 2) {
return 0;
}
const timeSpan = this.metrics.createTimes[this.metrics.createTimes.length - 1] -
this.metrics.createTimes[0];
if (timeSpan === 0) {
return 0;
}
return (this.metrics.createTimes.length / timeSpan) * 1000;
}
/** 计算访问频率(个/秒) */
getAccessFrequency(): number {
if (this.metrics.accessTimes.length < 2) {
return 0;
}
const timeSpan = this.metrics.accessTimes[this.metrics.accessTimes.length - 1] -
this.metrics.accessTimes[0];
if (timeSpan === 0) {
return 0;
}
return (this.metrics.accessTimes.length / timeSpan) * 1000;
}
}
//#endregion
//#region 第二阶段:智能特性
/**
* 智能池管理器
* 添加学习、预热、类型分析功能
*/
class SmartPoolManager {
private pools: Map<string, DynamicPool<any>> = new Map();
private typeProfiles: Map<string, TypeProfile> = new Map();
private historyData: Map<string, HistoryData> = new Map();
private learningEnabled = true;
private readonly HISTORY_KEY = 'ecs_pool_history';
constructor() {
this.loadHistory();
}
/** 获取或创建池 */
getPool<T>(typeName: string, factory: () => T, config?: Partial<PoolConfig>): DynamicPool<T> {
if (!this.pools.has(typeName)) {
// 从历史数据学习
const history = this.historyData.get(typeName);
const learnedConfig = this.learnFromHistory(typeName, history);
// 合并配置
const finalConfig = { ...learnedConfig, ...config };
const pool = new DynamicPool(typeName, factory, finalConfig);
this.pools.set(typeName, pool);
console.log(`[SmartPool] 创建池: ${typeName}`, finalConfig);
}
return this.pools.get(typeName)!;
}
/** 从历史数据学习 */
private learnFromHistory(typeName: string, history?: HistoryData): Partial<PoolConfig> {
if (!history || !this.learningEnabled) {
return {};
}
const config: Partial<PoolConfig> = {};
// 根据历史峰值设置预热和最大容量
if (history.peakCount > 0) {
config.preWarmSize = Math.floor(history.avgCount * 0.5);
config.maxSize = Math.floor(history.peakCount * 1.2);
config.minSize = Math.floor(history.avgCount * 0.2);
console.log(`[SmartPool] ${typeName} 从历史学习: 峰值=${history.peakCount}, 平均=${history.avgCount}`);
}
return config;
}
/** 分析类型特征 */
analyzeType(typeName: string): TypeProfile {
const pool = this.pools.get(typeName);
if (!pool) {
return this.createDefaultProfile(typeName);
}
const metrics = pool.getMetrics();
const frequency = pool.getCreateFrequency();
// 计算复用率
const reuseRate = metrics.recycleCount > 0
? metrics.hitCount / metrics.recycleCount
: 0;
// 分类
let category: TypeProfile['category'] = 'normal';
if (frequency > 50) {
category = 'high-frequency';
} else if (reuseRate < 0.2) {
category = 'rare';
} else if (metrics.createCount < 10) {
category = 'long-lived';
}
const profile: TypeProfile = {
typeName,
category,
peakRate: frequency,
reuseRate,
avgMemorySize: metrics.estimatedItemSize,
shouldPool: this.shouldPool(category, reuseRate, frequency)
};
this.typeProfiles.set(typeName, profile);
return profile;
}
/** 判断是否应该池化 */
private shouldPool(category: string, reuseRate: number, frequency: number): boolean {
// 高频对象必须池化
if (category === 'high-frequency') {
return true;
}
// 复用率太低不池化
if (reuseRate < 0.1) {
return false;
}
// 长生命周期且数量少不池化
if (category === 'long-lived' && frequency < 1) {
return false;
}
return true;
}
/** 创建默认特征 */
private createDefaultProfile(typeName: string): TypeProfile {
return {
typeName,
category: 'normal',
peakRate: 0,
reuseRate: 0,
avgMemorySize: 0,
shouldPool: true
};
}
/** 场景感知预热 */
onSceneChange(sceneType: string, hints: Map<string, number>): void {
console.log(`[SmartPool] 场景切换: ${sceneType}`);
hints.forEach((count, typeName) => {
const pool = this.pools.get(typeName);
if (pool) {
pool.preWarm(count);
}
});
}
/** 保存历史数据 */
saveHistory(): void {
const history: Record<string, HistoryData> = {};
this.pools.forEach((pool, typeName) => {
const metrics = pool.getMetrics();
history[typeName] = {
typeName,
peakCount: metrics.maxSize,
avgCount: Math.floor((metrics.hitCount + metrics.missCount) / 2),
lastPlayTime: Date.now()
};
});
try {
localStorage.setItem(this.HISTORY_KEY, JSON.stringify(history));
console.log('[SmartPool] 历史数据已保存');
} catch (e) {
console.warn('[SmartPool] 保存历史数据失败', e);
}
}
/** 加载历史数据 */
private loadHistory(): void {
try {
const data = localStorage.getItem(this.HISTORY_KEY);
if (data) {
const history = JSON.parse(data);
Object.entries(history).forEach(([typeName, data]) => {
this.historyData.set(typeName, data as HistoryData);
});
console.log(`[SmartPool] 加载历史数据: ${this.historyData.size} 个类型`);
}
} catch (e) {
console.warn('[SmartPool] 加载历史数据失败', e);
}
}
/** 获取所有池的统计信息 */
getAllMetrics(): Map<string, PoolMetrics> {
const result = new Map<string, PoolMetrics>();
this.pools.forEach((pool, typeName) => {
result.set(typeName, pool.getMetrics());
});
return result;
}
/** 清空所有池 */
clearAll(): void {
this.pools.forEach(pool => pool.clear());
console.log('[SmartPool] 所有池已清空');
}
/** 清理所有池中的过期对象 */
cleanupAllStale(maxAge: number = 60000): number {
let totalCleaned = 0;
this.pools.forEach(pool => {
totalCleaned += pool.cleanupStale(maxAge);
});
if (totalCleaned > 0) {
console.log(`[SmartPool] 总共清理了 ${totalCleaned} 个过期对象`);
}
return totalCleaned;
}
/** 获取总内存占用 */
getTotalMemory(): number {
let total = 0;
this.pools.forEach(pool => {
total += pool.getMetrics().totalMemory;
});
return total;
}
/** 获取类型分析报告 */
getTypeProfiles(): Map<string, TypeProfile> {
// 更新所有类型的分析
this.pools.forEach((pool, typeName) => {
this.analyzeType(typeName);
});
return new Map(this.typeProfiles);
}
}
//#endregion
//#region 第三阶段:可选配置
/** 池配置装饰器 */
export function poolConfig(config: PoolConfig) {
return function <T extends { new(...args: any[]): {} }>(constructor: T) {
// 将配置存储到构造函数上
(constructor as any).__poolConfig = config;
return constructor;
};
}
/** 池提示装饰器 */
export function poolHint(hint: PoolHint) {
return function <T extends { new(...args: any[]): {} }>(constructor: T) {
// 将提示存储到构造函数上
(constructor as any).__poolHint = hint;
return constructor;
};
}
/** 获取类的池配置 */
export function getPoolConfig(constructor: any): PoolConfig | undefined {
return constructor.__poolConfig;
}
/** 获取类的池提示 */
export function getPoolHint(constructor: any): PoolHint | undefined {
return constructor.__poolHint;
}
//#endregion
//#region 全局协调器
/**
* 全局池协调器
* 管理总内存预算,协调各池之间的资源分配
*/
class GlobalPoolCoordinator {
private smartManager: SmartPoolManager;
private totalBudget = 100 * 1024 * 1024; // 100MB默认预算
private cleanupTimer: any = null;
private autoCleanupEnabled = false;
constructor() {
this.smartManager = new SmartPoolManager();
}
/** 获取或创建池 */
getPool<T>(typeName: string, factory: () => T, ctor?: any): DynamicPool<T> {
let config: Partial<PoolConfig> = {};
let hint: PoolHint | undefined;
// 读取装饰器配置
if (ctor) {
const decoratorConfig = getPoolConfig(ctor);
if (decoratorConfig) {
config = decoratorConfig;
}
hint = getPoolHint(ctor);
if (hint) {
// 应用提示
if (hint.expectedPeak) {
config.maxSize = hint.expectedPeak;
config.preWarmSize = Math.floor(hint.expectedPeak * 0.3);
}
if (hint.highFrequency) {
config.minSize = 50;
config.maxSize = config.maxSize || 1000;
}
}
}
return this.smartManager.getPool(typeName, factory, config);
}
/** 场景切换 */
onSceneChange(sceneType: string, hints: Map<string, number>): void {
this.smartManager.onSceneChange(sceneType, hints);
}
/** 获取智能管理器 */
getSmartManager(): SmartPoolManager {
return this.smartManager;
}
/** 设置总内存预算 */
setTotalBudget(bytes: number): void {
this.totalBudget = bytes;
console.log(`[GlobalCoordinator] 设置内存预算: ${(bytes / 1024 / 1024).toFixed(2)}MB`);
// 检查是否超出预算
this.checkMemoryBudget();
}
/** 检查内存预算 */
private checkMemoryBudget(): void {
const usedMemory = this.smartManager.getTotalMemory();
const usagePercent = (usedMemory / this.totalBudget) * 100;
if (usedMemory > this.totalBudget) {
console.warn(`[GlobalCoordinator] 内存超出预算! 使用: ${(usedMemory / 1024 / 1024).toFixed(2)}MB / ${(this.totalBudget / 1024 / 1024).toFixed(2)}MB (${usagePercent.toFixed(1)}%)`);
// 触发清理
this.smartManager.cleanupAllStale(30000); // 清理30秒未使用的对象
// 再次检查
const newUsed = this.smartManager.getTotalMemory();
if (newUsed > this.totalBudget) {
console.warn(`[GlobalCoordinator] 清理后仍超出预算,考虑增加预算或优化对象使用`);
}
} else if (usagePercent > 80) {
console.warn(`[GlobalCoordinator] 内存使用率较高: ${usagePercent.toFixed(1)}%`);
}
}
/** 获取当前内存使用情况 */
getMemoryUsage(): { used: number; budget: number; percent: number } {
const used = this.smartManager.getTotalMemory();
return {
used,
budget: this.totalBudget,
percent: (used / this.totalBudget) * 100
};
}
/** 启用自动清理(定期清理过期对象) */
enableAutoCleanup(interval: number = 60000): void {
if (this.autoCleanupEnabled) {
console.warn('[GlobalCoordinator] 自动清理已启用');
return;
}
this.autoCleanupEnabled = true;
this.cleanupTimer = setInterval(() => {
this.smartManager.cleanupAllStale();
this.checkMemoryBudget();
}, interval);
console.log(`[GlobalCoordinator] 启用自动清理,间隔: ${interval}ms`);
}
/** 禁用自动清理 */
disableAutoCleanup(): void {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
this.autoCleanupEnabled = false;
console.log('[GlobalCoordinator] 禁用自动清理');
}
/** 保存历史数据 */
saveHistory(): void {
this.smartManager.saveHistory();
}
}
//#endregion
//#region 导出单例
/** 全局池协调器实例 */
export const globalPoolCoordinator = new GlobalPoolCoordinator();
//#endregion

View File

@@ -0,0 +1,11 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "88db857b-ee25-442e-aef3-64a3b1e474de",
"files": [],
"subMetas": {},
"userData": {
"simulateGlobals": []
}
}