# Memory Management

> Mobile memory management including resource disposal, memory pooling, texture management, and leak prevention

- **Type:** Skill
- **Install:** `agentstack add skill-bullish0x-gamestudio-memory-management`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [bullish0x](https://agentstack.voostack.com/s/bullish0x)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [bullish0x](https://github.com/bullish0x)
- **Source:** https://github.com/bullish0x/GameStudio/tree/main/.agents/skills/memory-management

## Install

```sh
agentstack add skill-bullish0x-gamestudio-memory-management
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Mobile Memory Management

## When to Use

Use this skill when:
- Optimizing for mobile memory constraints
- Preventing memory leaks
- Managing texture memory
- Implementing object pooling
- Handling low memory warnings
- Optimizing garbage collection

## Core Principles

1. **Proactive Disposal**: Clean up resources explicitly
2. **Object Pooling**: Reuse instead of allocate
3. **Texture Management**: Compress and limit VRAM
4. **Lazy Loading**: Load resources on demand
5. **Memory Monitoring**: Track and respond to pressure
6. **GC Optimization**: Minimize garbage collection pauses

## Memory Management Implementation

### 1. Resource Manager

```typescript
// memory/ResourceManager.ts
export interface Disposable {
  dispose(): void;
}

export class ResourceManager {
  private resources = new Map();
  private refCounts = new Map();
  private memoryUsage = 0;

  register(id: string, resource: T): T {
    if (this.resources.has(id)) {
      // Resource already exists, increment ref count
      this.refCounts.set(id, (this.refCounts.get(id) ?? 0) + 1);
      return this.resources.get(id) as T;
    }

    this.resources.set(id, resource);
    this.refCounts.set(id, 1);

    // Track memory usage
    if ('memory' in resource) {
      this.memoryUsage += (resource as any).memory;
    }

    return resource;
  }

  get(id: string): T | undefined {
    return this.resources.get(id) as T | undefined;
  }

  release(id: string): void {
    const refCount = this.refCounts.get(id) ?? 0;

    if (refCount  {
    const resources: Array = [];

    for (const [id, resource] of this.resources) {
      if ('memory' in resource) {
        resources.push({ id, memory: (resource as any).memory });
      }
    }

    return resources.sort((a, b) => b.memory - a.memory);
  }
}
```

### 2. Texture Memory Manager

```typescript
// memory/TextureManager.ts
export class TextureManager {
  private textures = new Map();
  private vramUsage = 0;
  private readonly MAX_VRAM_MB = 256; // Mobile limit

  constructor(private resourceManager: ResourceManager) {}

  async load(url: string, options: { compress?: boolean } = {}): Promise {
    // Check if already loaded
    if (this.textures.has(url)) {
      return this.textures.get(url)!;
    }

    // Check VRAM limit
    if (this.vramUsage >= this.MAX_VRAM_MB * 1024 * 1024) {
      this.evictLeastRecentlyUsed();
    }

    const loader = new THREE.TextureLoader();
    const texture = await loader.loadAsync(url);

    // Apply mobile-friendly settings
    texture.generateMipmaps = true;
    texture.minFilter = THREE.LinearMipmapLinearFilter;
    texture.magFilter = THREE.LinearFilter;

    // Compress if requested
    if (options.compress) {
      this.applyCompression(texture);
    }

    // Calculate and track VRAM usage
    const memorySize = this.calculateTextureSize(texture);
    this.vramUsage += memorySize;

    // Register with resource manager
    this.resourceManager.register(url, {
      dispose: () => {
        texture.dispose();
        this.vramUsage -= memorySize;
        this.textures.delete(url);
      },
    });

    this.textures.set(url, texture);
    return texture;
  }

  private calculateTextureSize(texture: THREE.Texture): number {
    const image = texture.image;
    if (!image) return 0;

    const width = image.width || 512;
    const height = image.height || 512;

    // RGBA = 4 bytes per pixel, plus mipmaps (~1.33x)
    return width * height * 4 * 1.33;
  }

  private applyCompression(texture: THREE.Texture): void {
    // Note: Actual compression requires compressed texture formats
    // This is a placeholder for format selection logic

    // For mobile, prefer:
    // - ASTC on iOS
    // - ETC2 on Android
    // - Fallback to lower resolution

    // Example: Reduce resolution
    if (texture.image) {
      const maxSize = 1024;
      if (texture.image.width > maxSize || texture.image.height > maxSize) {
        texture.image.width = Math.min(texture.image.width, maxSize);
        texture.image.height = Math.min(texture.image.height, maxSize);
        texture.needsUpdate = true;
      }
    }
  }

  private evictLeastRecentlyUsed(): void {
    // Simple strategy: remove oldest texture
    const firstKey = this.textures.keys().next().value;
    if (firstKey) {
      this.resourceManager.release(firstKey);
    }
  }

  release(url: string): void {
    this.resourceManager.release(url);
  }

  getVRAMUsage(): number {
    return this.vramUsage;
  }

  getVRAMLimit(): number {
    return this.MAX_VRAM_MB * 1024 * 1024;
  }

  getVRAMUsagePercent(): number {
    return (this.vramUsage / this.getVRAMLimit()) * 100;
  }
}
```

### 3. Object Pooling

```typescript
// memory/ObjectPool.ts
export interface Poolable {
  reset(): void;
}

export class ObjectPool {
  private pool: T[] = [];
  private active = new Set();
  private factory: () => T;
  private initialSize: number;
  private maxSize: number;

  constructor(
    factory: () => T,
    initialSize: number = 10,
    maxSize: number = 100
  ) {
    this.factory = factory;
    this.initialSize = initialSize;
    this.maxSize = maxSize;

    // Pre-allocate initial pool
    for (let i = 0; i  0) {
      obj = this.pool.pop()!;
    } else if (this.active.size );
    }
    this.entity.active = false;
  }
}
```

### 4. Memory Monitor

```typescript
// memory/MemoryMonitor.ts
export interface MemoryInfo {
  usedJSHeapSize: number; // Bytes
  totalJSHeapSize: number;
  jsHeapSizeLimit: number;
  usagePercent: number;
}

export class MemoryMonitor {
  private checkInterval: number;
  private intervalId: number | null = null;
  private listeners = new Set void>();
  private warningThreshold = 0.85; // 85% usage
  private criticalThreshold = 0.95; // 95% usage

  constructor(checkInterval: number = 5000) {
    this.checkInterval = checkInterval;
  }

  start(): void {
    if (this.intervalId !== null) return;

    this.intervalId = window.setInterval(() => {
      this.checkMemory();
    }, this.checkInterval);

    // Initial check
    this.checkMemory();
  }

  stop(): void {
    if (this.intervalId !== null) {
      clearInterval(this.intervalId);
      this.intervalId = null;
    }
  }

  private checkMemory(): void {
    const info = this.getMemoryInfo();

    // Notify listeners
    for (const listener of this.listeners) {
      listener(info);
    }

    // Check thresholds
    if (info.usagePercent >= this.criticalThreshold) {
      this.onCriticalMemory();
    } else if (info.usagePercent >= this.warningThreshold) {
      this.onWarningMemory();
    }
  }

  getMemoryInfo(): MemoryInfo {
    if ('memory' in performance) {
      const memory = (performance as any).memory;

      return {
        usedJSHeapSize: memory.usedJSHeapSize,
        totalJSHeapSize: memory.totalJSHeapSize,
        jsHeapSizeLimit: memory.jsHeapSizeLimit,
        usagePercent: (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100,
      };
    }

    // Fallback for browsers without memory API
    return {
      usedJSHeapSize: 0,
      totalJSHeapSize: 0,
      jsHeapSizeLimit: 0,
      usagePercent: 0,
    };
  }

  private onWarningMemory(): void {
    console.warn('Memory usage above warning threshold');
    // Trigger gentle cleanup
  }

  private onCriticalMemory(): void {
    console.error('Memory usage critical!');
    // Trigger aggressive cleanup
    this.forceGarbageCollection();
  }

  private forceGarbageCollection(): void {
    // Trigger GC indirectly by nulling references
    // Note: Can't force GC in JavaScript, but can encourage it

    // Clear caches, pools, etc.
    window.dispatchEvent(new CustomEvent('memory:critical'));
  }

  onMemoryChange(listener: (info: MemoryInfo) => void): () => void {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }

  setWarningThreshold(percent: number): void {
    this.warningThreshold = Math.max(0, Math.min(1, percent));
  }

  setCriticalThreshold(percent: number): void {
    this.criticalThreshold = Math.max(0, Math.min(1, percent));
  }
}
```

### 5. Geometry Pool Manager

```typescript
// memory/GeometryManager.ts
export class GeometryManager {
  private geometries = new Map();
  private instances = new Map();

  constructor(private resourceManager: ResourceManager) {}

  createBox(width: number, height: number, depth: number): THREE.BufferGeometry {
    const key = `box:${width}:${height}:${depth}`;
    return this.getOrCreate(key, () => new THREE.BoxGeometry(width, height, depth));
  }

  createSphere(radius: number, segments: number = 32): THREE.BufferGeometry {
    const key = `sphere:${radius}:${segments}`;
    return this.getOrCreate(key, () => new THREE.SphereGeometry(radius, segments, segments));
  }

  createPlane(width: number, height: number): THREE.BufferGeometry {
    const key = `plane:${width}:${height}`;
    return this.getOrCreate(key, () => new THREE.PlaneGeometry(width, height));
  }

  private getOrCreate(key: string, factory: () => THREE.BufferGeometry): THREE.BufferGeometry {
    let geometry = this.geometries.get(key);

    if (!geometry) {
      geometry = factory();
      this.geometries.set(key, geometry);
      this.instances.set(geometry, 0);

      // Register for cleanup
      this.resourceManager.register(key, {
        dispose: () => {
          geometry!.dispose();
          this.geometries.delete(key);
          this.instances.delete(geometry!);
        },
      });
    }

    // Increment instance count
    this.instances.set(geometry, (this.instances.get(geometry) ?? 0) + 1);

    return geometry;
  }

  release(geometry: THREE.BufferGeometry): void {
    const count = (this.instances.get(geometry) ?? 0) - 1;

    if (count ();
  private instances = new Map();

  constructor(private resourceManager: ResourceManager) {}

  createStandard(options: {
    color?: THREE.ColorRepresentation;
    map?: THREE.Texture;
    metalness?: number;
    roughness?: number;
  }): THREE.MeshStandardMaterial {
    const key = this.hashOptions('standard', options);
    return this.getOrCreate(key, () => new THREE.MeshStandardMaterial(options));
  }

  createBasic(options: {
    color?: THREE.ColorRepresentation;
    map?: THREE.Texture;
  }): THREE.MeshBasicMaterial {
    const key = this.hashOptions('basic', options);
    return this.getOrCreate(key, () => new THREE.MeshBasicMaterial(options));
  }

  private hashOptions(type: string, options: any): string {
    // Simple hash of material options
    return `${type}:${JSON.stringify(options)}`;
  }

  private getOrCreate(key: string, factory: () => T): T {
    let material = this.materials.get(key) as T;

    if (!material) {
      material = factory();
      this.materials.set(key, material);
      this.instances.set(material, 0);

      // Register for cleanup
      this.resourceManager.register(key, {
        dispose: () => {
          material!.dispose();
          this.materials.delete(key);
          this.instances.delete(material!);
        },
      });
    }

    // Increment instance count
    this.instances.set(material, (this.instances.get(material) ?? 0) + 1);

    return material;
  }

  release(material: THREE.Material): void {
    const count = (this.instances.get(material) ?? 0) - 1;

    if (count  new PooledEntity(world), 50, 200);

// Spawn entity
const pooled = entityPool.acquire();
const entity = pooled.entity;
entity.addComponent(new Transform(new Vector3(0, 0, 0)));
entity.active = true;

// Return to pool when destroyed
entity.active = false;
entityPool.release(pooled);

// Example 5: Memory monitoring
const memoryMonitor = new MemoryMonitor(5000);

memoryMonitor.onMemoryChange((info) => {
  console.log(`Memory usage: ${info.usagePercent.toFixed(1)}%`);

  if (info.usagePercent > 90) {
    // Aggressive cleanup
    entityPool.releaseAll();
    textureManager.getResourcesByMemory().slice(0, 10).forEach((res) => {
      resourceManager.release(res.id);
    });
  }
});

memoryMonitor.start();

// Example 6: Respond to low memory
window.addEventListener('memory:critical', () => {
  // Clear all pools
  entityPool.clear();

  // Release unused textures
  textureManager.evictLeastRecentlyUsed();

  // Clear geometry cache
  geometryManager.clear();

  // Clear material cache
  materialManager.clear();

  // Force scene cleanup
  scene.traverse((obj) => {
    if (obj instanceof THREE.Mesh) {
      obj.geometry.dispose();

      if (Array.isArray(obj.material)) {
        obj.material.forEach((mat) => mat.dispose());
      } else {
        obj.material.dispose();
      }
    }
  });
});

// Example 7: Disposable scene manager
class DisposableScene {
  private scene: THREE.Scene;
  private objects = new Set();

  constructor() {
    this.scene = new THREE.Scene();
  }

  add(object: THREE.Object3D): void {
    this.scene.add(object);
    this.objects.add(object);
  }

  remove(object: THREE.Object3D): void {
    this.scene.remove(object);
    this.objects.delete(object);
    this.disposeObject(object);
  }

  private disposeObject(object: THREE.Object3D): void {
    object.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        geometryManager.release(child.geometry);
        if (Array.isArray(child.material)) {
          child.material.forEach((mat) => materialManager.release(mat));
        } else {
          materialManager.release(child.material);
        }
      }
    });
  }

  dispose(): void {
    for (const object of this.objects) {
      this.disposeObject(object);
    }

    this.objects.clear();
    this.scene.clear();
  }
}
```

## Checklist

- [ ] Implement ResourceManager
- [ ] Create TextureManager with VRAM tracking
- [ ] Set up object pooling
- [ ] Add memory monitoring
- [ ] Create geometry/material managers
- [ ] Handle low memory events
- [ ] Implement proper disposal
- [ ] Test for memory leaks
- [ ] Profile memory usage
- [ ] Add memory usage UI

## Common Pitfalls

1. **Missing dispose calls**: Memory leaks
2. **No ref counting**: Premature disposal
3. **Unbounded pools**: Memory growth
4. **Texture duplication**: Wasted VRAM
5. **No memory monitoring**: Crashes on low memory
6. **Event listener leaks**: Accumulating handlers
7. **Circular references**: GC can't collect

## Performance Tips

### Memory Efficiency
- Pool frequently created objects
- Share geometries and materials
- Compress textures for mobile
- Monitor VRAM usage
- Dispose resources explicitly

### Leak Prevention
- Use ref counting for shared resources
- Remove event listeners
- Clear intervals and timeouts
- Null references when done
- Profile with DevTools

### GC Optimization
- Minimize allocations in hot paths
- Reuse objects via pooling
- Avoid creating temporary objects
- Use typed arrays
- Batch operations

### Mobile Considerations
- Lower VRAM limits (256MB typical)
- More aggressive texture compression
- Smaller object pools
- More frequent cleanup
- Monitor device memory API

## Related Skills

- `battery-optimization` - Power management
- `mobile-performance` - General optimization
- `threejs-texture-management` - Texture optimization
- `threejs-geometry-management` - Geometry sharing
- `ecs-performance` - ECS memory patterns

## References

- JavaScript memory management
- WebGL memory limits
- Mobile VRAM constraints
- Object pooling patterns

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [bullish0x](https://github.com/bullish0x)
- **Source:** [bullish0x/GameStudio](https://github.com/bullish0x/GameStudio)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-bullish0x-gamestudio-memory-management
- Seller: https://agentstack.voostack.com/s/bullish0x
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
