对象池内存管理优化
This commit is contained in:
@@ -8,24 +8,41 @@ export class ComponentPool<T extends Component> {
|
|||||||
private createFn: () => T;
|
private createFn: () => T;
|
||||||
private resetFn?: (component: T) => void;
|
private resetFn?: (component: T) => void;
|
||||||
private maxSize: number;
|
private maxSize: number;
|
||||||
|
private minSize: number;
|
||||||
|
private growthFactor: number;
|
||||||
|
|
||||||
|
private stats = {
|
||||||
|
totalCreated: 0,
|
||||||
|
totalAcquired: 0,
|
||||||
|
totalReleased: 0
|
||||||
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
createFn: () => T,
|
createFn: () => T,
|
||||||
resetFn?: (component: T) => void,
|
resetFn?: (component: T) => void,
|
||||||
maxSize: number = 1000
|
maxSize: number = 1000,
|
||||||
|
minSize: number = 10,
|
||||||
|
growthFactor: number = 1.5
|
||||||
) {
|
) {
|
||||||
this.createFn = createFn;
|
this.createFn = createFn;
|
||||||
this.resetFn = resetFn;
|
this.resetFn = resetFn;
|
||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
|
this.minSize = Math.max(1, minSize);
|
||||||
|
this.growthFactor = Math.max(1.1, growthFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取一个组件实例
|
* 获取一个组件实例
|
||||||
*/
|
*/
|
||||||
acquire(): T {
|
acquire(): T {
|
||||||
|
this.stats.totalAcquired++;
|
||||||
|
|
||||||
if (this.pool.length > 0) {
|
if (this.pool.length > 0) {
|
||||||
return this.pool.pop()!;
|
return this.pool.pop()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.stats.totalCreated++;
|
||||||
|
|
||||||
return this.createFn();
|
return this.createFn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,20 +50,41 @@ export class ComponentPool<T extends Component> {
|
|||||||
* 释放一个组件实例回池中
|
* 释放一个组件实例回池中
|
||||||
*/
|
*/
|
||||||
release(component: T): void {
|
release(component: T): void {
|
||||||
if (this.pool.length < this.maxSize) {
|
this.stats.totalReleased++;
|
||||||
if (this.resetFn) {
|
|
||||||
this.resetFn(component);
|
if (this.pool.length >= this.maxSize) {
|
||||||
}
|
return;
|
||||||
this.pool.push(component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.resetFn) {
|
||||||
|
this.resetFn(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pool.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预填充对象池
|
* 预填充对象池
|
||||||
*/
|
*/
|
||||||
prewarm(count: number): void {
|
prewarm(count: number): void {
|
||||||
for (let i = 0; i < count && this.pool.length < this.maxSize; i++) {
|
const targetCount = Math.min(count, this.maxSize);
|
||||||
this.pool.push(this.createFn());
|
|
||||||
|
for (let i = this.pool.length; i < targetCount; i++) {
|
||||||
|
const component = this.createFn();
|
||||||
|
if (this.resetFn) {
|
||||||
|
this.resetFn(component);
|
||||||
|
}
|
||||||
|
this.pool.push(component);
|
||||||
|
this.stats.totalCreated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动收缩池大小
|
||||||
|
*/
|
||||||
|
shrink(): void {
|
||||||
|
while (this.pool.length > this.minSize) {
|
||||||
|
this.pool.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +108,35 @@ export class ComponentPool<T extends Component> {
|
|||||||
getMaxSize(): number {
|
getMaxSize(): number {
|
||||||
return this.maxSize;
|
return this.maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取统计信息
|
||||||
|
*/
|
||||||
|
getStats() {
|
||||||
|
const hitRate = this.stats.totalAcquired === 0
|
||||||
|
? 0
|
||||||
|
: (this.stats.totalAcquired - this.stats.totalCreated) / this.stats.totalAcquired;
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalCreated: this.stats.totalCreated,
|
||||||
|
totalAcquired: this.stats.totalAcquired,
|
||||||
|
totalReleased: this.stats.totalReleased,
|
||||||
|
hitRate: hitRate,
|
||||||
|
currentSize: this.pool.length,
|
||||||
|
maxSize: this.maxSize,
|
||||||
|
minSize: this.minSize,
|
||||||
|
utilizationRate: this.pool.length / this.maxSize
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件使用追踪
|
||||||
|
*/
|
||||||
|
interface ComponentUsageTracker {
|
||||||
|
createCount: number;
|
||||||
|
releaseCount: number;
|
||||||
|
lastAccessTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,6 +145,10 @@ export class ComponentPool<T extends Component> {
|
|||||||
export class ComponentPoolManager {
|
export class ComponentPoolManager {
|
||||||
private static instance: ComponentPoolManager;
|
private static instance: ComponentPoolManager;
|
||||||
private pools = new Map<string, ComponentPool<any>>();
|
private pools = new Map<string, ComponentPool<any>>();
|
||||||
|
private usageTracker = new Map<string, ComponentUsageTracker>();
|
||||||
|
|
||||||
|
private autoCleanupInterval = 60000;
|
||||||
|
private lastCleanupTime = 0;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
@@ -95,9 +166,16 @@ export class ComponentPoolManager {
|
|||||||
componentName: string,
|
componentName: string,
|
||||||
createFn: () => T,
|
createFn: () => T,
|
||||||
resetFn?: (component: T) => void,
|
resetFn?: (component: T) => void,
|
||||||
maxSize?: number
|
maxSize?: number,
|
||||||
|
minSize?: number
|
||||||
): void {
|
): void {
|
||||||
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize));
|
this.pools.set(componentName, new ComponentPool(createFn, resetFn, maxSize, minSize));
|
||||||
|
|
||||||
|
this.usageTracker.set(componentName, {
|
||||||
|
createCount: 0,
|
||||||
|
releaseCount: 0,
|
||||||
|
lastAccessTime: Date.now()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,6 +183,9 @@ export class ComponentPoolManager {
|
|||||||
*/
|
*/
|
||||||
acquireComponent<T extends Component>(componentName: string): T | null {
|
acquireComponent<T extends Component>(componentName: string): T | null {
|
||||||
const pool = this.pools.get(componentName);
|
const pool = this.pools.get(componentName);
|
||||||
|
|
||||||
|
this.trackUsage(componentName, 'create');
|
||||||
|
|
||||||
return pool ? pool.acquire() : null;
|
return pool ? pool.acquire() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,11 +194,71 @@ export class ComponentPoolManager {
|
|||||||
*/
|
*/
|
||||||
releaseComponent<T extends Component>(componentName: string, component: T): void {
|
releaseComponent<T extends Component>(componentName: string, component: T): void {
|
||||||
const pool = this.pools.get(componentName);
|
const pool = this.pools.get(componentName);
|
||||||
|
|
||||||
|
this.trackUsage(componentName, 'release');
|
||||||
|
|
||||||
if (pool) {
|
if (pool) {
|
||||||
pool.release(component);
|
pool.release(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追踪使用情况
|
||||||
|
*/
|
||||||
|
private trackUsage(componentName: string, action: 'create' | 'release'): void {
|
||||||
|
let tracker = this.usageTracker.get(componentName);
|
||||||
|
|
||||||
|
if (!tracker) {
|
||||||
|
tracker = {
|
||||||
|
createCount: 0,
|
||||||
|
releaseCount: 0,
|
||||||
|
lastAccessTime: Date.now()
|
||||||
|
};
|
||||||
|
this.usageTracker.set(componentName, tracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'create') {
|
||||||
|
tracker.createCount++;
|
||||||
|
} else {
|
||||||
|
tracker.releaseCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker.lastAccessTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动清理(定期调用)
|
||||||
|
*/
|
||||||
|
public update(): void {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (now - this.lastCleanupTime < this.autoCleanupInterval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [name, tracker] of this.usageTracker.entries()) {
|
||||||
|
const inactive = now - tracker.lastAccessTime > 120000;
|
||||||
|
|
||||||
|
if (inactive) {
|
||||||
|
const pool = this.pools.get(name);
|
||||||
|
if (pool) {
|
||||||
|
pool.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastCleanupTime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取热点组件列表
|
||||||
|
*/
|
||||||
|
public getHotComponents(threshold: number = 100): string[] {
|
||||||
|
return Array.from(this.usageTracker.entries())
|
||||||
|
.filter(([_, tracker]) => tracker.createCount > threshold)
|
||||||
|
.map(([name]) => name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预热所有池
|
* 预热所有池
|
||||||
*/
|
*/
|
||||||
@@ -137,10 +278,28 @@ export class ComponentPoolManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重置管理器,移除所有注册的池
|
* 重置管理器
|
||||||
*/
|
*/
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.pools.clear();
|
this.pools.clear();
|
||||||
|
this.usageTracker.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局统计信息
|
||||||
|
*/
|
||||||
|
getGlobalStats() {
|
||||||
|
const stats: any[] = [];
|
||||||
|
|
||||||
|
for (const [name, pool] of this.pools.entries()) {
|
||||||
|
stats.push({
|
||||||
|
componentName: name,
|
||||||
|
poolStats: pool.getStats(),
|
||||||
|
usage: this.usageTracker.get(name)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,7 +317,7 @@ export class ComponentPoolManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取池利用率信息(用于调试)
|
* 获取池利用率信息
|
||||||
*/
|
*/
|
||||||
getPoolUtilization(): Map<string, { used: number; total: number; utilization: number }> {
|
getPoolUtilization(): Map<string, { used: number; total: number; utilization: number }> {
|
||||||
const utilization = new Map();
|
const utilization = new Map();
|
||||||
@@ -167,7 +326,7 @@ export class ComponentPoolManager {
|
|||||||
const maxSize = pool.getMaxSize();
|
const maxSize = pool.getMaxSize();
|
||||||
const used = maxSize - available;
|
const used = maxSize - available;
|
||||||
const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0;
|
const utilRate = maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||||
|
|
||||||
utilization.set(name, {
|
utilization.set(name, {
|
||||||
used: used,
|
used: used,
|
||||||
total: maxSize,
|
total: maxSize,
|
||||||
@@ -183,11 +342,11 @@ export class ComponentPoolManager {
|
|||||||
getComponentUtilization(componentName: string): number {
|
getComponentUtilization(componentName: string): number {
|
||||||
const pool = this.pools.get(componentName);
|
const pool = this.pools.get(componentName);
|
||||||
if (!pool) return 0;
|
if (!pool) return 0;
|
||||||
|
|
||||||
const available = pool.getAvailableCount();
|
const available = pool.getAvailableCount();
|
||||||
const maxSize = pool.getMaxSize();
|
const maxSize = pool.getMaxSize();
|
||||||
const used = maxSize - available;
|
const used = maxSize - available;
|
||||||
|
|
||||||
return maxSize > 0 ? (used / maxSize * 100) : 0;
|
return maxSize > 0 ? (used / maxSize * 100) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -874,8 +874,8 @@ export class Entity {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁实体
|
* 销毁实体
|
||||||
*
|
*
|
||||||
* 移除所有组件、子实体并标记为已销毁。
|
* 移除所有组件、子实体并标记为已销毁
|
||||||
*/
|
*/
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
if (this._isDestroyed) {
|
if (this._isDestroyed) {
|
||||||
@@ -883,29 +883,66 @@ export class Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._isDestroyed = true;
|
this._isDestroyed = true;
|
||||||
|
|
||||||
const childrenToDestroy = [...this._children];
|
const childrenToDestroy = [...this._children];
|
||||||
for (const child of childrenToDestroy) {
|
for (const child of childrenToDestroy) {
|
||||||
child.destroy();
|
child.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._parent) {
|
if (this._parent) {
|
||||||
this._parent.removeChild(this);
|
this._parent.removeChild(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.removeAllComponents();
|
this.removeAllComponents();
|
||||||
|
|
||||||
if (this.scene) {
|
if (this.scene) {
|
||||||
if (this.scene.querySystem) {
|
if (this.scene.querySystem) {
|
||||||
this.scene.querySystem.removeEntity(this);
|
this.scene.querySystem.removeEntity(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.scene.entities) {
|
if (this.scene.entities) {
|
||||||
this.scene.entities.remove(this);
|
this.scene.entities.remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量销毁所有子实体
|
||||||
|
*/
|
||||||
|
public destroyAllChildren(): void {
|
||||||
|
if (this._children.length === 0) return;
|
||||||
|
|
||||||
|
const scene = this.scene;
|
||||||
|
const toDestroy: Entity[] = [];
|
||||||
|
|
||||||
|
const collectChildren = (entity: Entity) => {
|
||||||
|
for (const child of entity._children) {
|
||||||
|
toDestroy.push(child);
|
||||||
|
collectChildren(child);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
collectChildren(this);
|
||||||
|
|
||||||
|
for (const entity of toDestroy) {
|
||||||
|
entity._isDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entity of toDestroy) {
|
||||||
|
entity.removeAllComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scene) {
|
||||||
|
for (const entity of toDestroy) {
|
||||||
|
scene.entities.remove(entity);
|
||||||
|
scene.querySystem.removeEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.clearSystemEntityCaches();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._children.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较实体
|
* 比较实体
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decor
|
|||||||
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||||
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
||||||
import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
|
import { IncrementalSerializer, IncrementalSnapshot, IncrementalSerializationOptions } from './Serialization/IncrementalSerializer';
|
||||||
|
import { ComponentPoolManager } from './Core/ComponentPool';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 游戏场景默认实现类
|
* 游戏场景默认实现类
|
||||||
@@ -185,14 +186,13 @@ export class Scene implements IScene {
|
|||||||
* 更新场景
|
* 更新场景
|
||||||
*/
|
*/
|
||||||
public update() {
|
public update() {
|
||||||
// 更新实体列表(处理延迟操作)
|
ComponentPoolManager.getInstance().update();
|
||||||
|
|
||||||
this.entities.updateLists();
|
this.entities.updateLists();
|
||||||
|
|
||||||
// 更新实体处理器
|
|
||||||
if (this.entityProcessors != null)
|
if (this.entityProcessors != null)
|
||||||
this.entityProcessors.update();
|
this.entityProcessors.update();
|
||||||
|
|
||||||
// 更新实体处理器后处理
|
|
||||||
if (this.entityProcessors != null)
|
if (this.entityProcessors != null)
|
||||||
this.entityProcessors.lateUpdate();
|
this.entityProcessors.lateUpdate();
|
||||||
}
|
}
|
||||||
@@ -273,13 +273,35 @@ export class Scene implements IScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量销毁实体
|
||||||
|
*/
|
||||||
|
public destroyEntities(entities: Entity[]): void {
|
||||||
|
if (entities.length === 0) return;
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
entity._isDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
entity.removeAllComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
this.entities.remove(entity);
|
||||||
|
this.querySystem.removeEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.querySystem.clearCache();
|
||||||
|
this.clearSystemEntityCaches();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从场景中删除所有实体
|
* 从场景中删除所有实体
|
||||||
*/
|
*/
|
||||||
public destroyAllEntities() {
|
public destroyAllEntities() {
|
||||||
this.entities.removeAllEntities();
|
this.entities.removeAllEntities();
|
||||||
|
|
||||||
// 清理查询系统中的实体引用和缓存
|
|
||||||
this.querySystem.setEntities([]);
|
this.querySystem.setEntities([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user