Memory Leaks | මතක කාන්දු හඳුනාගැනීම සහ නිවැරදි කිරීම - SC Guide

Memory Leaks | මතක කාන්දු හඳුනාගැනීම සහ නිවැරදි කිරීම - SC Guide

මතක කාන්දු (Memory Leaks): ඒවා හඳුනාගෙන නිවැරදි කරමු!

ආයුබෝවන් මගේ යාළුවනේ, කොහොමද ඔයාලට? 😊 අපි software developer කෙනෙක් විදියට වැඩ කරනකොට නිතරම වගේ අපේ application එකේ performance එක ගැන හිතනවා. ඒත් සමහර වෙලාවට අපිට මුණගැහෙන ප්‍රශ්නයක් තමයි, කාලයක් යනකොට අපේ application එක ටිකෙන් ටික slow වෙන එක, සමහර විට ගිහිල්ලා crash වෙන එක. මේකට එක ප්‍රධානම හේතුවක් තමයි "Memory Leaks". මේක ඇත්තටම අපේ software එකේ health එකට කෙලින්ම බලපාන දෙයක්. සාමාන්‍යයෙන් අපේ දවසේ වැඩ කටයුතු වලදී මේ වගේ issues මුණගැහුණම හරියටම මොකද වුණේ කියලා තේරුම් ගන්න ටිකක් අමාරුයි. ඒ නිසා, අද අපි බලමු මොනවද මේ Memory Leaks කියන්නේ, ඒවා ඇයි හැදෙන්නේ, කොහොමද හොයන්නේ, ඒ වගේම කොහොමද ඒවා fix කරන්නේ කියලා. විශේෂයෙන්ම, Memory Leaks හොයන්න පාවිච්චි කරන Profilers ගැනත් අපි ගැඹුරින් කතා කරමු.

අපේ රටේ developers ලා විදියට, මේ වගේ පොදු ගැටලු ගැන හොඳ අවබෝධයක් තියෙන එක අපේ code quality එකට විතරක් නෙමෙයි, අපේ professional career එකටත් ගොඩක් වැදගත්. මොකද, හොඳින් optimize කරපු, leak-free application එකක් කියන්නේ user experience එකටත්, ඒ වගේම business එකේ success එකටත් කෙලින්ම බලපාන දෙයක්නේ. ඉතින්, අපි Memory Leaks කියන මාතෘකාවට කිමිදෙමු!

මොනවද මේ Memory Leaks කියන්නේ? 🤔

සරලවම කිව්වොත්, Memory Leak එකක් කියන්නේ, අපේ program එකට වෙන් කරපු (allocated) memory කොටසක්, ඒකේ වැඩේ ඉවර වුණාට පස්සේ ආයෙත් free කරන්නේ නැතුව දිගටම තියාගෙන ඉන්න එක. සාමාන්‍යයෙන්, program එකකට memory අවශ්‍ය වුණාම, operating system එකෙන් ඒකට ඒක වෙන් කරනවා. වැඩේ ඉවර වුණාම, ඒ memory කොටස ආයෙත් system එකට නිදහස් කරන්න ඕනේ අනිත් programs වලට පාවිච්චි කරන්න පුළුවන් වෙන්න. ඒත්, මේ Memory Leak එකකදී වෙන්නේ, program එකට තවදුරටත් අවශ්‍ය නැති වුණත්, ඒ memory කොටස දිගටම අල්ලාගෙන ඉන්න එකයි.

මේක තේරුම් ගන්න පොඩි analogy එකක් ගමු. හිතන්න, ඔයා පොතක් කියවන්න පුස්තකාලෙන් පොත් ටිකක් ගත්තා කියලා. 📚 කියවලා ඉවර වෙලා ඒ පොත් ටික ආයෙත් පුස්තකාලෙට දෙන්නේ නැතුව ගෙදර තියාගත්තොත් මොකද වෙන්නේ? පුස්තකාලේට අලුත් පොත් ගන්න ඉඩ මදි වෙනවා, අනිත් අයට පොත් ගන්න බැරි වෙනවා. ඒ වගේම ඔයාගේ ගෙදරත් පොත් ගොඩ ගැහිලා අවුල් වෙනවා. Software වලදී, මේ "පොත්" තමයි "memory". program එක මේ memory නිදහස් නොකරනකොට, system එකේ තියෙන available memory ප්‍රමාණය ටිකෙන් ටික අඩු වෙනවා. අන්තිමට, system එකට තවත් memory වෙන් කරන්න බැරි වුණාම, application එක slow වෙලා, වැඩ කරන්න බැරි වෙලා, අන්තිමට crash වෙනවා. මේක තමයි අපිට ගොඩක් වෙලාවට දකින්න ලැබෙන තත්ත්වය.

සමහර developers ලා හිතනවා Garbage Collector එකක් තියෙන languages (JavaScript, Java, C#, Python වගේ) වල Memory Leaks හැදෙන්නේ නැහැ කියලා. ඒත් ඒක වැරදි මතයක්. Garbage Collector එකක් කරන්නේ, කිසිම reference එකක් නැති objects free කරන එකයි. ඒත් Memory Leak එකකදී, object එක use වෙන්නේ නැති වුණත්, ඒකට අනවශ්‍ය විදියට තවම reference එකක් තියෙන නිසා Garbage Collector එකට ඒක free කරන්න බැරි වෙනවා. ඉතින්, Garbage Collector එකක් තිබුණත්, අපි පරිස්සමෙන් code කරන්න ඕනේ.

Memory Leaks හැදෙන්නෙ ඇයි? 🧐

Memory Leaks ඇති වෙන්න ප්‍රධාන හේතු කීපයක් තියෙනවා. මේවා ගැන හොඳ අවබෝධයක් තියෙන එක ඒවා වළක්වා ගන්නත්, detect කරගන්නත් ගොඩක් වැදගත්.

Timers and Intervals

JavaScript වල setTimeout සහ setInterval වගේ functions පාවිච්චි කරනකොටත් Memory Leaks ඇති වෙන්න පුළුවන්. උදාහරණයක් විදියට, component එකක් unmount වුණාට පස්සේත් setInterval එකක් දිගටම දුවනවා නම්, ඒ interval එක ඇතුළේ reference වෙන objects memory එකේ දිගටම තියෙන්න පුළුවන්. මේවා clearTimeout සහ clearInterval පාවිච්චි කරලා properly clear කරන්න ඕනේ.

// JavaScript Example: Uncleared Interval
let intervalId;
function startLogging() {
  intervalId = setInterval(() => {
    console.log('Logging data...');
  }, 1000);
}

function stopLogging() {
  clearInterval(intervalId); // Crucial to clear the interval
}

// If stopLogging is not called, the interval keeps running and potentially leaks resources.

Caches and Global State

Global variables වලට හෝ static collections (e.g., ArrayList, HashMap) වලට objects add කරලා, ඒ objects පස්සේ remove නොකරන එක. Cache එකක් implement කරනකොට, ඒකට උපරිම ප්‍රමාණයක් (maximum size) තියෙන්න ඕනේ. ඒ වගේම, cache එක පිරුණාම, අලුත් දේවල් add කරන්න ඉඩ හදන්න Least Recently Used (LRU) වගේ eviction policies implement කරන්න ඕනේ. Global state එක Minimize කරන එකත් Memory Leaks වළක්වා ගන්න ගොඩක් වැදගත්.

Circular References

Objects දෙකක් එකිනෙකාට reference කරනකොට (A object එක B object එකට reference කරනවා, ඒ වගේම B object එකත් A object එකට reference කරනවා) මේ වගේ තත්ත්වයක් ඇති වෙන්න පුළුවන්. Garbage Collector එකකට මේ වගේ circular references හඳුනාගන්න සමහර වෙලාවට අමාරුයි, විශේෂයෙන්ම පැරණි Garbage Collector implementations වලදී. මේවා නිසා අර objects දෙකම memory එකේ රැඳී තිබිය හැකියි, අවශ්‍ය නැති වුණත්. JavaScript වල closure වලදී, DOM element reference කරද්දී මේක වෙන්න පුළුවන්. Weak References හෝ ID මත පදනම් වූ references පාවිච්චි කරන එක මේකට විසඳුමක්.

Event Listeners / Subscriptions

User Interface (UI) development වලදී මේක නිතරම දකින්න පුළුවන් ප්‍රශ්නයක්. අපි UI component එකකට event listener (e.g., click event) එකක් add කරනවා. ඒත්, ඒ component එක DOM එකෙන් remove කරනකොට (unmount වෙනකොට) ඒ event listener එක remove නොකරන එකෙන් leak එකක් ඇති වෙන්න පුළුවන්. මොකද, listener එක තවමත් remove නොකරන නිසා, Garbage Collector එකට අර component එක memory එකෙන් අයින් කරන්න බැරි වෙනවා. JavaScript (React, Angular, Vue) වගේ frameworks වලදී මේක විශේෂයෙන්ම සැලකිලිමත් වෙන්න ඕනේ. Component එකේ lifecycle methods (componentWillUnmount in React, ngOnDestroy in Angular) පාවිච්චි කරලා listeners remove කරන එක අත්‍යාවශ්‍යයි.

// JavaScript Example: Unremoved Event Listener
class MyComponent extends React.Component {
  componentDidMount() {
    // This listener will stay in memory even if MyComponent is unmounted
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    console.log('Window resized!');
  };

  render() {
    return <div>My Component</div>;
  }
}

// Corrected version
class MyComponentFixed extends React.Component {
  componentDidMount() {
    window.addEventListener('resize', this.handleResize);
  }

  componentWillUnmount() {
    // Remove the listener when the component unmounts
    window.removeEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    console.log('Window resized!');
  };

  render() {
    return <div>My Component</div>;
  }
}

Unreleased Resources

File handles, database connections, network sockets, streams වගේ දේවල් අපි program එකකදී පාවිච්චි කරනවා. මේවා පාවිච්චි කරලා ඉවර වුණාම properly close නොකරන එක Memory Leaks වලට හේතු වෙනවා. උදාහරණයක් විදියට, database connection එකක් open කරලා, ඒක close නොකරන එකෙන්, ඒ connection එකට වෙන් කරපු memory ටික දිගටම block වෙනවා. මේකට විසඳුම තමයි try-finally blocks හෝ using statements (C#), with statements (Python) වගේ දේවල් පාවිච්චි කරලා, resource එකේ වැඩේ ඉවර වුණ ගමන් ඒක නිදහස් කරන එක.

// Java Example: Unreleased Database Connection
Connection conn = null;
try {
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
    // Do something with the connection
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    if (conn != null) {
        try {
            conn.close(); // Crucial to close the connection
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Memory Leaks හොයන්නේ කොහොමද? (Profiler Exercise) 🔍

මෙන්න මේක තමයි මේ article එකේ වැදගත්ම කොටස. Memory Leaks අතින් හොයන එක ටිකක් අමාරු වැඩක්. මොකද, අපිට code එක බැලුවාට හැම වෙලාවෙම leak එකක් කොතනද කියලා හරියටම කියන්න බැහැ. ඒත් අපිට "Profilers" පාවිච්චි කරන්න පුළුවන්. Profilers කියන්නේ, අපේ program එක run වෙනකොට ඒකේ performance, memory usage, CPU usage වගේ දේවල් analyze කරන්න පුළුවන් tools.

ප්‍රධාන Programming Languages සහ Platforms වලට පාවිච්චි කරන Profilers කීපයක්:

  • JavaScript (Web Browsers): Chrome DevTools (Memory tab), Firefox Developer Tools (Memory tab)
  • Java: JProfiler, VisualVM, Eclipse Memory Analyzer Tool (MAT)
  • .NET: Visual Studio Diagnostic Tools, ANTS Memory Profiler, dotMemory
  • C/C++: Valgrind (Linux), Visual Studio Debugger, AddressSanitizer
  • Python: memory_profiler, objgraph

අපි දැන් Memory Leaks එකක් හොයන්න profiler එකක් (Chrome DevTools) කොහොමද පාවිච්චි කරන්නේ කියලා පියවරෙන් පියවර බලමු. මේක තමයි ඔයාලට කරන්න පුළුවන් practical exercise එක.

පියවරෙන් පියවර Leak එකක් හොයමු (Chrome DevTools උදාහරණය)

  1. Application එක විවෘත කරන්න: ඔයාගේ Web application එක Chrome browser එකේ open කරගන්න.
  2. DevTools වෙත යන්න: F12 (හෝ Ctrl+Shift+I / Cmd+Option+I) ඔබලා Chrome DevTools open කරගන්න. ඊට පස්සේ "Memory" tab එකට යන්න.
  3. Leak එකක් ඇති විය හැකි ක්‍රියාවක් සිදු කරන්න: දැන්, ඔයාගේ application එකේ memory leak එකක් ඇති වෙන්න පුළුවන් ක්‍රියාවක් කරන්න. උදාහරණයක් විදියට, list එකකට අලුත් items add කරන්න, page එකක් navigate කරලා ආපහු එන්න, modal එකක් open කරලා close කරන්න, විශාල data set එකක් load කරන්න වගේ දෙයක්. මේ ක්‍රියාව කිහිප වතාවක්ම (2-3 වතාවක්) කරන්න, leak එකක් confirm කරගන්න.
  4. Leaking Objects හඳුනාගන්න: "Objects allocated between snapshots" view එකේදී, "Size Delta" (memory usage හි වෙනස) සහ "# New" (අලුතින් එකතු වූ objects ගණන) තීරුවල වැඩි වීමක් තියෙන objects හොයන්න. විශේෂයෙන්ම, ඔයා ක්‍රියාත්මක කරපු action එකට සම්බන්ධ objects වල ගණන දිගටම වැඩි වෙනවා නම්, ඒක leak එකක ලක්ෂණයක්. උදාහරණයක් විදියට, "Detached DOM tree" (DOM එකෙන් අයින් කරලා තිබුණත් memory එකේ තියෙන elements) වගේ දේවල් දැක්කොත් ඒක leak එකක් වෙන්න පුළුවන්.
  5. Retained Path එක විශ්ලේෂණය කරන්න: Leaking කියලා සැක හිතෙන object එකක් click කරලා expand කරන්න. ඒකේ "Retainers" හෝ "Retained Path" section එක බලන්න. මේකෙන් පෙන්නන්නේ මොන object එකද මේ leaking object එකට reference කරගෙන ඉන්නේ කියලා. මේ "retaining path" එකෙන් ඔයාට code එකේ කොතැනද leak එක සිද්ධ වෙන්නේ කියලා හඳුනාගන්න පුළුවන්. මේක තමයි leak එක identify කරන critical step එක.

Snapshot දෙක සන්සන්දනය කරන්න: දැන් ඔයාට Snapshot #1 සහ Snapshot #2 දෙකම පේනවා ඇති. Snapshot #2 තෝරලා, ඒකේ "Comparison" view එකට යන්න. මේකෙන් ඔයාට "Objects allocated between snapshots" කියන option එක තෝරන්න පුළුවන්. මේකෙන් පෙන්නන්නේ Snapshot #1 ගත්තට පස්සේ අලුතින් allocated වෙලා, Snapshot #2 වෙනකොටත් memory එකේ තියෙන objects මොනවද කියලා. (Images will vary based on actual leak situation)

Chrome DevTools Snapshot Comparison

දෙවැනි Heap Snapshot එක ගන්න: ක්‍රියාව ඉවර වුණාට පස්සේ, ආයෙත් "Memory" tab එකට ගිහිල්ලා, "Take snapshot" button එක click කරලා තවත් Heap snapshot එකක් ගන්න. (Snapshot #2).

Chrome DevTools Heap Snapshot 2

මුල් Heap Snapshot එක ගන්න: "Memory" tab එකේ වම් පැත්තේ තියෙන "Heap snapshot" radio button එක තෝරලා, ඊට පස්සේ "Take snapshot" button එක click කරන්න. මේක ඔයාගේ application එකේ මේ වෙලාවේ තියෙන සියලුම JavaScript objects සහ DOM nodes වල memory state එක record කරනවා. මේක base-line එක විදියට හිතන්න. (Snapshot #1).

Chrome DevTools Heap Snapshot 1

මේක ටිකක් practice කරන්න ඕනේ දෙයක්. නමුත් මේ ක්‍රමවේදය හරියට තේරුම් ගත්තොත්, ඔයාට ඕනෑම Web application එකක memory leaks ඉතා සාර්ථකව හොයාගන්න පුළුවන් වේවි. අනිත් programming languages වල profilres වලත් මේ හා සමානම concepts සහ features තමයි තියෙන්නේ.

Memory Leaks Fix කරන්නේ කොහොමද? ✅

Memory Leaks detect කරාට පස්සේ, ඒවා fix කරන එක තමයි ඊළඟ step එක. මේවා fix කරන්න පුළුවන් පොදු උපාය මාර්ග කීපයක් තියෙනවා:

Review Third-Party Libraries

සමහර වෙලාවට, ඔබ පාවිච්චි කරන third-party libraries වලත් Memory Leaks තියෙන්න පුළුවන්. මේවා හඳුනාගන්න අමාරුයි, නමුත් ඒවායේ documentation බලන්න, GitHub issues check කරන්න, හෝ ඒවායේ community forums වල මේ වගේ ප්‍රශ්න ගැන කතා කරලා තියෙනවද කියලා බලන්න. සමහර වෙලාවට, library එකේ bug එකක් නිසා වෙන්න පුළුවන්. ඒ වගේ වෙලාවට, library එක update කරන්න, හෝ වෙනත් library එකක් පාවිච්චි කරන්න සිද්ධ වෙන්න පුළුවන්.

Clear Timers

setTimeout සහ setInterval functions පාවිච්චි කරලා ඉවර වුණාම, අනිවාර්යයෙන්ම clearTimeout සහ clearInterval වලින් ඒ timer එක clear කරන්න. විශේෂයෙන්ම, component එකක් destroy වෙනකොට හෝ user session එකක් අවසන් වෙනකොට මේවා clear කරලා තියෙන්න ඕනේ.

Manage Caches and Global State

ඔබේ application එකේ cache එකක් පාවිච්චි කරනවා නම්, ඒකට maximum size එකක් දෙන්න. ඒ වගේම, cache එක පිරුණාම, අලුත් දේවල් add කරන්න ඉඩ හදන්න LRU (Least Recently Used) වගේ eviction policies implement කරන්න. Global variables භාවිතය අවම කරන්න, මොකද ඒවාට පහසුවෙන් අනවශ්‍ය references ගොඩනැගෙන්න පුළුවන්. Global state එකක් අවශ්‍ය නම්, ඒක properly manage කරන්න. (e.g., Redux in React).

Break Circular References

Objects අතර circular references ඇති වීම අවම කරන්න. පුළුවන් නම්, weak references පාවිච්චි කරන්න. JavaScript වලදී, DOM element එකකට direct reference එකක් තියාගන්නවා වෙනුවට, element එකේ ID එක හෝ class එක වගේ identifier එකක් තියාගෙන, අවශ්‍ය වෙලාවට document.getElementById() වගේ දේවල් වලින් search කරගන්න පුළුවන්. Java වලදී WeakReference class එක මේ සඳහා පාවිච්චි කරන්න පුළුවන්.

Detach Event Listeners

ඔබ UI component එකකට event listener එකක් add කරා නම්, ඒ component එක DOM එකෙන් remove කරනකොට (unmount කරනකොට) ඒ event listener එක අනිවාර්යයෙන්ම remove කරන්න. removeEventListener වගේ function එකක් addEventListener එකට matching call එකක් විදියට පාවිච්චි කරන්න. JavaScript frameworks වලදී, component lifecycle methods (e.g., componentWillUnmount in React, ngOnDestroy in Angular) මේ සඳහා හොඳම තැන.

Proper Resource Management

Files, network connections, database connections, streams වගේ දේවල් පාවිච්චි කරලා ඉවර වුණ ගමන්ම නිවැරදිව close හෝ dispose කරන්න. try-with-resources (Java), using statements (C#), හෝ with statements (Python) වගේ language constructs පාවිච්චි කරන එක මේකට ගොඩක් උදව් වෙනවා. මේවා අනිවාර්යයෙන්ම resource එක නිදහස් කරනවා, exception එකක් ආවත්.

අවසාන වශයෙන්...

ඉතින් මචංලා, Memory Leaks කියන්නේ software development වලදි මගහරින්න බැරි, ඒත් සාර්ථකව විසඳන්න පුළුවන් පොදු ගැටළුවක්. ඒවා හඳුනාගන්නත්, fix කරන්නත් අපිට හොඳ tools (profilers) තියෙනවා. Profiler එකක් පාවිච්චි කරන එක ඔබේ development process එකේ කොටසක් කරගන්න එක ඔබේ application වල performance එකටත්, stability එකටත් ගොඩක් හොඳයි. නිතරම ඔබේ code එකේ resource management ගැන, event listeners ගැන, global state ගැන සැලකිලිමත් වෙන්න.

ඔබේ application එක වේගවත්ව, කාර්යක්ෂමව තියාගන්න, Memory Leaks ගැන දැනුවත් වෙන්න! මේ article එක ඔයාලට වැදගත් වෙන්න ඇති කියලා හිතනවා. ඔයාලගේ project වලදී මෙහෙම leaks අහු වෙලා තියෙනවද? එහෙම නම්, කොහොමද fix කරේ? නැත්නම් මේක ගැන ඔයාලට තියෙන අදහස් මොනවද? පහළ තියෙන comment section එකේ share කරන්න. ඔබේ අත්දැකීම් අනිත් අයටත් ගොඩක් වැදගත් වෙයි. තවත් මේ වගේ ප්‍රයෝජනවත් article එකකින් හමුවෙමු!