> For the complete documentation index, see [llms.txt](https://channyeintun.gitbook.io/blogs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://channyeintun.gitbook.io/blogs/javascript/js-closure-memory-leaks.md).

# JS Closure Memory Leaks

{% embed url="<https://x.com/jarredsumner/status/2017825694731145388>" %}

### နိဒါန်း

X မှာ closure ကြောင့် memory leak ဖြစ်တဲ့အကြောင်း status တစ်ခုတွေ့လိုက်လို့ အဲ့ဒီအကြောင်းနည်းနည်းပြောချင်ပါတယ်။ JavaScript မှာ event listener တွေနဲ့ arrow function တွေသုံးတဲ့အခါ memory leak ဖြစ်နိုင်တယ်။ ဒီ article မှာ memory leak ဖြစ်တဲ့အကြောင်းရင်းနဲ့ `bind()` method နဲ့ ဘယ်လိုဖြေရှင်းမလဲဆိုတာ အသေးစိတ်ပြောပြပေးပါမယ်။

### Problem: Arrow Function နဲ့ Memory Leak

#### ပြဿနာရှိတဲ့ Code

```javascript
if (signal) signal.addEventListener('abort', () => controller.abort());

const timeout = setTimeout(() => controller.abort(), ms);
```

#### Memory Leak ဖြစ်တဲ့အကြောင်းရင်း

Arrow function `() => controller.abort()` က **closure** တစ်ခု create လုပ်တယ်။ Closure က surrounding scope မှာရှိတဲ့ variable အားလုံးကို capture လုပ်ထားတယ်:

* `controller` (AbortController instance)
* `this` (surrounding context)
* `init` (request initialization options)
* `options` (potentially large objects)
* Request body နဲ့ တခြား large object တွေ

#### Memory Leak Chain

**၁. Signal က long-lived object ဖြစ်နေတယ်**

* AbortSignal တွေက မိနစ်တွေ၊ နာရီတွေအထိ live ဖြစ်နေနိုင်တယ်

**၂. Event listener က arrow function ကို reference လုပ်ထားတယ်**

* Signal က listener ကို မဖျက်သရွေ့ arrow function က memory မှာ ရှိနေမယ်

**၃. Arrow function ရဲ့ closure က variable အားလုံးကို hold လုပ်ထားတယ်**

* Closure က scope မှာရှိတဲ့ variable တိုင်းကို reference လုပ်ထားတယ်

**၄. Request ပြီးသွားပြီးနောက်ပိုင်းမှာလည်း signal က live ဖြစ်နေသေးတယ်**

* User က AbortSignal ကို ကြာကြာ hold ထားနိုင်တယ်

**၅. ရလဒ်: အရာအားလုံး memory မှာ stuck ဖြစ်နေတယ် 💥**

* Garbage collector က ဘာမှ ဖျက်မပစ်နိုင်ဘူး
* Memory usage က တက်နေတယ်

### Solution: bind() Method သုံးခြင်း

#### Fix လုပ်ပြီးသား Code

```javascript
// Avoid creating a closure over `this`, `init`, or `options` to prevent memory leaks.
const abort = controller.abort.bind(controller);
if (signal) signal.addEventListener('abort', abort, { once: true });

const timeout = setTimeout(abort, ms);
```

#### bind() က Memory Leak ကို ဘယ်လို fix လုပ်သလဲ

**၁. Closure မလုပ်ဘူး**

```javascript
const abort = controller.abort.bind(controller);
```

Bound function က:

* ✅ `controller` ကိုပဲ reference လုပ်တယ် (via `[[BoundThis]]`)
* ✅ Surrounding scope ကို capture မလုပ်ဘူး
* ✅ `this`, `init`, `options` တွေကို hold မထားဘူး

**၂. { once: true } option**

javascript

```javascript
signal.addEventListener('abort', abort, { once: true });
```

ဒီ option က:

* Event fire ဖြစ်တာနဲ့ listener ကို auto remove လုပ်ပေးတယ်
* Memory cleanup က ချက်ချင်း ဖြစ်တယ်

### Technical အသေးစိတ်

#### Arrow Function vs Bound Function

**❌ Arrow Function (Memory Leak ဖြစ်တယ်)**

````javascript
() => controller.abort()
```

ECMAScript Spec အရ (§15.3.3):
- **Lexical environment** တစ်ခု create လုပ်တယ်
- Scope မှာရှိတဲ့ variable အားလုံးကို capture လုပ်တယ်
- Closure ဖြစ်တယ်

Capture လုပ်တဲ့ အရာတွေ:
```
├─ controller
├─ this
├─ init (request options)
├─ options
├─ request body (အရမ်းကြီးနိုင်တယ်!)
└─ ... scope မှာရှိတဲ့ အရာအားလုံး
````

**✅ Bound Function (Memory Leak မဖြစ်ဘူး)**

````javascript
controller.abort.bind(controller)
```

ECMAScript Spec အရ (§20.2.3.2, §10.4.1):
- **BoundFunction exotic object** တစ်ခု create လုပ်တယ်
- Internal slot တွေပဲ store လုပ်တယ်:
  - `[[BoundTargetFunction]]`: original function
  - `[[BoundThis]]`: controller object
  - `[[BoundArguments]]`: empty array
- Lexical environment capture မလုပ်ဘူး

Store လုပ်တဲ့ အရာတွေ:
```
└─ controller (via [[BoundThis]])
````

#### Memory Footprint နှိုင်းယှဉ်ချက်

**Scenario: Request 1000 ခု လုပ်တယ်**

```javascript
// BAD: Arrow function သုံးခြင်း
for (let i = 0; i < 1000; i++) {
  const signal = new AbortSignal();
  const hugeData = new ArrayBuffer(10 * 1024 * 1024); // 10MB
  
  signal.addEventListener('abort', () => controller.abort());
  // ^ Closure က hugeData ကို capture လုပ်ထားတယ်!
  // Request ပြီးသွားပြီးနောက်ပိုင်းမှာလည်း hugeData က memory မှာ ရှိနေသေးတယ်!
}
// Memory usage: ~10GB (1000 × 10MB)

// GOOD: bind() သုံးခြင်း
for (let i = 0; i < 1000; i++) {
  const signal = new AbortSignal();
  const hugeData = new ArrayBuffer(10 * 1024 * 1024);
  
  const abort = controller.abort.bind(controller);
  signal.addEventListener('abort', abort, { once: true });
  // ^ Closure မရှိဘူး၊ hugeData ကို GC က delete လုပ်ပစ်နိုင်တယ်!
}
// Memory usage: နည်းနည်းပဲ (controller references တွေပဲ)
```

### ECMAScript Specification အရ ဆိုရင်

#### Arrow Function Closure (§15.3.3)

Arrow function က **lexical scoping** သုံးတယ်:

**၁. Function create လုပ်တဲ့အခါ:**

* Surrounding scope ရဲ့ environment record ကို capture လုပ်တယ်
* Variable တွေရဲ့ reference ကို store လုပ်တယ်

**၂. Function ကို call လုပ်တဲ့အခါ:**

* Captured environment ကို သုံးတယ်
* Variable တွေကို original scope ကနေ read လုပ်တယ်

**၃. Memory management:**

* Function က live ဖြစ်နေသရွေ့ environment က memory မှာ ရှိနေမယ်
* Garbage collector က ဘာမှ delete မလုပ်နိုင်ဘူး

#### Bound Function (§20.2.3.2, §10.4.1)

`bind()` က **exotic object** တစ်ခု create လုပ်တယ်:

**၁. bind() call လုပ်တဲ့အခါ:**

javascript

```javascript
const bound = func.bind(thisArg, ...args)
```

Spec က ဒီလို လုပ်တယ်:

* Target function ကို store လုပ်တယ် (`[[BoundTargetFunction]]`)
* `thisArg` ကို store လုပ်တယ် (`[[BoundThis]]`)
* Argument တွေကို store လုပ်တယ် (`[[BoundArguments]]`)
* **Environment ကို capture မလုပ်ဘူး**

**၂. Bound function ကို call လုပ်တဲ့အခါ:**

* Store လုပ်ထားတဲ့ `[[BoundThis]]` ကို `this` အနေနဲ့ သုံးတယ်
* Original function ကို call လုပ်တယ်
* Normal `this` binding rules ကို ignore လုပ်တယ်

**၃. Memory management:**

* `[[BoundThis]]` object ကိုပဲ reference လုပ်တယ်
* တခြား variable တွေကို capture မလုပ်ဘူး
* Memory footprint က သေးတယ်

### Real-World ဥပမာ

#### Web Application: Fetch Requests

javascript

```javascript
async function fetchWithTimeout(url, options = {}) {
  const controller = new AbortController();
  const { signal: userSignal, timeout = 5000 } = options;
  
  // ❌ MEMORY LEAK VERSION
  if (userSignal) {
    userSignal.addEventListener('abort', () => controller.abort());
    // Problem: Arrow function က controller, options, url စတဲ့
    // variable အားလုံးကို capture လုပ်ထားတယ်
  }
  
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  // Problem: Arrow function က controller နဲ့ timeout ကို
  // capture လုပ်ထားတယ်
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    throw error;
  }
}

// ✅ FIXED VERSION
async function fetchWithTimeout(url, options = {}) {
  const controller = new AbortController();
  const { signal: userSignal, timeout = 5000 } = options;
  
  // Fix: bind() သုံးပြီး closure ကို ရှောင်တယ်
  const abort = controller.abort.bind(controller);
  
  if (userSignal) {
    userSignal.addEventListener('abort', abort, { once: true });
  }
  
  const timeoutId = setTimeout(abort, timeout);
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    throw error;
  }
}
```

#### Performance Impact

**Memory usage test:**

javascript

```javascript
// Test setup
const iterations = 10000;
const largeData = new ArrayBuffer(1024 * 1024); // 1MB

// Test 1: Arrow function
console.time('Arrow Function');
for (let i = 0; i < iterations; i++) {
  const signal = new AbortController().signal;
  const controller = new AbortController();
  
  signal.addEventListener('abort', () => {
    controller.abort();
    // largeData ကို access မလုပ်ပေမယ့်
    // closure က capture လုပ်ထားတယ်
  });
}
console.timeEnd('Arrow Function');
// Result: Memory က တက်နေတယ်

// Test 2: bind()
console.time('Bound Function');
for (let i = 0; i < iterations; i++) {
  const signal = new AbortController().signal;
  const controller = new AbortController();
  
  const abort = controller.abort.bind(controller);
  signal.addEventListener('abort', abort);
}
console.timeEnd('Bound Function');
// Result: Memory က stable ဖြစ်တယ်
```

### Best Practices

#### ၁. Long-lived Event Listener တွေမှာ bind() သုံးပါ

javascript

```javascript
// ❌ BAD
class DataFetcher {
  constructor() {
    this.data = new Array(1000000); // Large array
    window.addEventListener('online', () => this.refetch());
  }
  
  refetch() {
    // Fetch data...
  }
}

// ✅ GOOD
class DataFetcher {
  constructor() {
    this.data = new Array(1000000);
    // bind() က this.data ကို capture မလုပ်ဘူး
    this.boundRefetch = this.refetch.bind(this);
    window.addEventListener('online', this.boundRefetch);
  }
  
  refetch() {
    // Fetch data...
  }
  
  destroy() {
    window.removeEventListener('online', this.boundRefetch);
  }
}
```

#### ၂. { once: true } option ကို သုံးပါ

```javascript
// Event တစ်ခါပဲ fire ဖြစ်မယ်ဆိုရင်
signal.addEventListener('abort', abort, { once: true });
// Listener က auto remove ဖြစ်သွားမယ်
```

#### ၃. Timer တွေမှာ bind() သုံးပါ

```javascript
// ❌ BAD
setTimeout(() => obj.method(), 1000);

// ✅ GOOD
const boundMethod = obj.method.bind(obj);
setTimeout(boundMethod, 1000);
```

#### ၄. Memory cleanup ကို သေချာ လုပ်ပါ

```javascript
class Component {
  constructor() {
    this.abort = this.controller.abort.bind(this.controller);
    this.signal.addEventListener('abort', this.abort);
  }
  
  destroy() {
    // Cleanup လုပ်ပါ
    this.signal.removeEventListener('abort', this.abort);
    this.controller = null;
  }
}
```

### နိဂုံး

#### အဓိက Point တွေ

**၁. Arrow function က closure create လုပ်တယ်**

* Surrounding scope မှာရှိတဲ့ variable အားလုံးကို capture လုပ်တယ်
* Memory leak ဖြစ်စေနိုင်တယ်

**၂. bind() က closure မ create ဘူး**

* `this` value ကိုပဲ bind လုပ်တယ်
* Memory footprint က သေးတယ်

**၃. Long-lived listener တွေမှာ bind() သုံးသင့်တယ်**

* Event listener တွေ
* Timer callback တွေ
* Global event handler တွေ

**၄. { once: true } option ကို မမေ့ပါနဲ့**

* Automatic cleanup ဖြစ်စေတယ်
* Memory leak ကို ပိုကာကွယ်ပေးတယ်

#### Performance Tips

* Large object တွေရှိတဲ့ scope မှာ arrow function မသုံးပါနဲ့
* Long-lived event တွေမှာ bind() သုံးပါ
* Cleanup logic ကို သေချာ ရေးပါ
* Memory profiler နဲ့ test လုပ်ပါ

#### Reference

**ECMAScript Specification:**

* §15.3.3: Arrow Function Definitions
* §20.2.3.2: Function.prototype.bind
* §10.4.1: Bound Function Exotic Objects
* §6.2.4: Reference Record

**အကြံပြုချက်:** Memory leak က web application တွေမှာ အဓိက performance problem တစ်ခု ဖြစ်တယ်။ `bind()` method ကို မှန်မှန်သုံးရင် ဒီ problem ကို ထိရောက်စွာ fix လုပ်နိုင်ပါတယ်။
