# Memory Leaks & Performance — GB4.7MFE

Last analyzed: 2026-02-23

---

## Known Leak Locations

### Subscription Leaks (no takeUntil)
- `features/gblayout/gblayout.component.ts:53`
  - `breakpointService.getCurrentBreakpoint().subscribe(...)` — no cleanup, no ngOnDestroy
- `features/gblayout/gbheader/gbusersetting/gbusersetting.component.ts:93`
  - `getportletservice().subscribe(...)` — no cleanup
- `features/login/service/login.service.ts:145,168,228,252,388,488,507,640,652`
  - Nested subscribes inside subscribes — service is singleton, leaks are permanent

### Event Listener Leak
- `features/gblayout/gblayout.component.ts:60`
  - `window.addEventListener('resize', () => this.adjustMenuTreeHeight())`
  - Arrow function — cannot be removed (new reference each time)
  - No removeEventListener in ngOnDestroy
  - **Fix:**
    ```typescript
    private resizeHandler = () => this.adjustMenuTreeHeight();
    ngAfterViewInit() { window.addEventListener('resize', this.resizeHandler); }
    ngOnDestroy() { window.removeEventListener('resize', this.resizeHandler); }
    // Better: replace with BreakpointObserver (already injected in this file)
    ```

### Timer Leaks
- `features/gbqrscanner/gbqrscanner.component.ts:122`
  - `setInterval(..., 500)` — cleared on scan success but NOT in ngOnDestroy
  - If user closes scanner before scanning → interval runs forever
- `features/gblayout/gbheader/gbusersetting/gbusersetting.component.ts:89`
  - `setTimeout(() => this.adjustTriggerWidth(), 100)` — untracked
- Also check: `gbaicoanalyst`, `gbtabcontainer`, `gbslickgrid` — multiple untracked timers

### Unbounded Data in Memory
- `projects/recruitment/dashboard/applicantdashboard/applicantdashboard.component.ts:56`
  - `signal<any[]>([])` holding full applicant list — no pagination
  - Fix: use server-side pagination, hold only current page in signal

### Missing OnPush
- `features/gbrichtexteditor/gbrichtexteditor.component.ts`
  - No `ChangeDetectionStrategy.OnPush` — every keystroke triggers full CD
  - Fix: Add `changeDetection: ChangeDetectionStrategy.OnPush`

---

## Mandatory Pattern for All Components

### Subscription cleanup
```typescript
private destroy$ = new Subject<void>();

ngOnDestroy(): void {
  this.destroy$.next();
  this.destroy$.complete();
}

// Every subscribe:
this.someService.getData()
  .pipe(takeUntil(this.destroy$))
  .subscribe(data => { ... });
```

### Timer cleanup
```typescript
private intervalId: ReturnType<typeof setInterval> | null = null;
private timeoutId: ReturnType<typeof setTimeout> | null = null;

ngOnDestroy(): void {
  if (this.intervalId) clearInterval(this.intervalId);
  if (this.timeoutId) clearTimeout(this.timeoutId);
}
```

### Event listener cleanup
```typescript
private boundHandler = this.myHandler.bind(this);
ngAfterViewInit() { window.addEventListener('resize', this.boundHandler); }
ngOnDestroy() { window.removeEventListener('resize', this.boundHandler); }
// Prefer: RxJS fromEvent(window, 'resize').pipe(takeUntil(this.destroy$))
```

---

## Performance Rules for New Modules

1. **Always use `ChangeDetectionStrategy.OnPush`** on every component
2. **Never subscribe without `takeUntil(this.destroy$)`** in components
3. **Prefer signals over BehaviorSubjects** for local component state (Angular 20)
4. **Use `computed()` for derived state** — don't recompute in templates
5. **Paginate large datasets** — never load full lists into memory signals
6. **Lazy-load heavy libraries** — d3, ag-grid, FullCalendar, jsPDF per route
7. **Prefer BreakpointObserver over raw window.addEventListener('resize')**
8. **Bundle budgets:** Add `budgets` in angular.json per project to catch bloat early

## Performance — Known Bundle Issues
- Both `@ngx-translate/core` and `@jsverse/transloco` bundled (duplicate i18n libs)
- d3, ag-grid, FullCalendar, jsPDF, Firebase all load at startup — need lazy loading
- No bundle size budgets set in angular.json
- 440 form JSON files in public/ — no cache headers in nginx config
