מה באמת אומר Memory Usage ב-Windows — איך לקרוא Working Set, Private Bytes, Commit ו-pagefile
· עודכן בתאריך: · Go Komura · Windows, Windows development, memory management, Working Set, Private Bytes, Commit, pagefile, performance monitoring, troubleshooting, Sysinternals
היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 4 Aug 2026)
- פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22175966)
מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.
Go Komura (2026). מה באמת אומר Memory Usage ב-Windows — איך לקרוא Working Set, Private Bytes, Commit ו-pagefile. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22175966 https://comcomponent.com/he/blog/windows-memory-usage-working-set-commit/
- DOI (הגרסה האחרונה)
- 10.5281/zenodo.22175966
- DOI (הגרסה הזו)
- 10.5281/zenodo.22175967
ב-Task Manager מופיע ש-Memory של process מסוים הוא 1.2GB. ב-Process Explorer, לעומת זאת, Working Set הוא 1.5GB, Private Bytes הוא 2.4GB, וב-VMMap ה-Size גדול עוד יותר. ברמת המערכת מופיע “Committed 19.6/31.8GB”.
אז כמה GB של זיכרון האפליקציה הזו באמת צורכת?
התשובה: המספר שצריך להסתכל עליו תלוי במה שרוצים לדעת. מדד השימוש משתנה לפי השאלה — כמה resident ב-RAM עכשיו, כמה הוקצה באופן פרטי ל-process הזה, כמה Commit Charge המערכת כבר סופרת, או רק איזה טווח virtual addresses שמור.
מה שמבלבל במדדי הזיכרון של Windows הוא שהכול מוצג תחת אותה מילה, Memory, אף שהם מודדים צירים נפרדים:
- כמה address space בשימוש
- כמה Commit נצרך
- האם זה כרגע resident ב-RAM הפיזי
- האם ה-page פרטי ל-process, או shareable
- כמה allocation נוספת המערכת כולה עוד יכולה לתמוך בה
המאמר מיועד למי שבודק גידול זיכרון באפליקציה או מחסור בזיכרון ברמת המערכת ב-Windows 10/11 וב-Windows Server הנוכחי, ומחבר בתמונה אחת את הקשרים בין Working Set, Private Working Set, Private Bytes, Commit, Virtual Bytes, pagefile, Available ו-Page Fault.
את הליך המעקב אחרי אובייקטי .NET שלא נאספים מכסה בפירוט “להבחין בין עיכוב GC ל-memory leak ב-.NET”, ואת ההפעלה המעשית של VMMap ו-Process Explorer מכסה “Process Explorer / Handle / VMMap בפועל”. המאמר הזה מתרכז בתנאי המוקדם של שניהם: איך לקרוא את המספרים מצד מערכת ההפעלה Windows.
1. השורה התחתונה קודם
- Working Set הוא קבוצת ה-pages שכרגע resident ב-RAM. הוא כולל לא רק pages פרטיים ל-process אלא גם pages שאפשר לשתף עם processes אחרים, כמו קוד DLL ו-memory-mapped files.1
- Private Working Set הוא החלק מתוך ה-Working Set שכרגע שייך רק לאותו process. הוא שימושי כקירוב ל-“ה-RAM ש-process זה לבדו תופס כרגע”, אבל זו לא הכמות הכוללת שהאפליקציה הקצתה.2
- Private Bytes הוא כמות ה-Commit הפרטית לאותו process. זה מדד נפרד משאלה אם הזיכרון כרגע resident ב-RAM. גם השדה
PagefileUsageבמבנה של Win32 API מייצג, ב-Windows הנוכחי, בפועל את אותו Commit Charge, ואינו מספר הבתים שנכתבו בפועל ל-pagefile.2 - “Committed X/Y” ב-Task Manager מציג את X כסך ה-Commit הנוכחי של המערכת ואת Y כתקרת ה-Commit. X אינו שימוש ב-pagefile. Y נקבע בערך לפי RAM ועוד pagefile.3
- Reserve ו-Commit הם דברים שונים. שמירה בלבד של טווח virtual addresses מניחה את הטווח בצד לשימוש עתידי; היא לא צורכת את אותה כמות לא של RAM ולא של תקרת ה-Commit.45
- Page Fault אינו בהכרח I/O לדיסק. יש soft fault, שאפשר לפתור בתוך ה-RAM, ו-hard fault שקורא מ-pagefile, מקובצי הרצה, מ-memory-mapped files וכדומה.16
- memory leak נשפט לא מקריאה אחת אלא מהמגמה כשחוזרים על אותו עומס. בפרט, עקבו אם Private Bytes והפירוט שלו ממשיכים לטפס במדרגות גם אחרי שהעיבוד נגמר, בלי לחזור לאותו steady state.
במשפט אחד: Working Set הוא “הכמות שנמצאת כרגע ב-RAM”, Private Bytes הוא “ה-Commit הפרטי ל-process הזה”, ו-Commit הוא “הכמות ש-committed ברמת המערכת”.
flowchart TB
accTitle: בחירת מדד הזיכרון הנכון של Windows
accDescr: איזה מדד לבדוק תלוי בשאלה אם רוצים residency ב-RAM, Commit פרטי ל-process, Commit ברמת המערכת, או טווח virtual addresses
question["מה רוצים לדעת על Memory Usage"]
question -->|הכמות כרגע ב-RAM| workingSet["Working Set"]
question -->|Commit הפרטי ל-process| privateBytes["Private Bytes"]
question -->|Commit ברמת המערכת| systemCommit["System Commit"]
question -->|טווח כתובות reserved| virtualBytes["Virtual Bytes / Reserved"]
workingSet --> resident["resident ב-RAM פיזי"]
privateBytes --> privateCommit["Commit פרטי ל-process"]
systemCommit --> commitLimit["השוואה מול Commit Limit"]
virtualBytes --> addressSpace["virtual address space"]
איור 1: פרקו תחילה את התצפית “הזיכרון גבוה” לארבע שאלות נפרדות.
2. פיצול Memory Usage לארבעה צירים
בהתחלה, חשבו על זיכרון Windows לא כעל “פס אחד” אלא לאורך ארבעה צירים.
flowchart TB
accTitle: ארבעה צירים עצמאיים לסיווג page אחד
accDescr: בדקו בנפרד את מצב ה-virtual address, את ה-backing של pages committed, את ה-residency ב-RAM הפיזי, ואת אפשרות השיתוף עם processes אחרים
page["הסתכלו על page אחד בארבעה צירים"]
page --> address["מצב הכתובת"]
address --> addressValues["Free / Reserved / Committed"]
page --> backing["backing"]
backing --> backingValues["Page-file-backed / File-backed"]
page --> residentAxis["residency ב-RAM"]
residentAxis --> residentValues["Resident / Not resident"]
page --> sharing["אפשרות שיתוף"]
sharing --> sharingValues["Private / Shareable"]
איור 2: גם ב-page אחד, מצב הכתובת, ה-backing, ה-residency ואפשרות השיתוף נקבעים כל אחד בנפרד.
Mapped אינו מצב כתובת לצד Free, Reserved ו-Committed — זו קטגוריה של אזור. גם pages ב-mapped view יכולים להיות Committed. באותו אופן, Private אינו מדיום backing אלא סיווג של אפשרות שיתוף. לכן קראו backing כ-Page-file-backed או File-backed, ואפשרות שיתוף כ-Private או Shareable, בנפרד.
צירוף ארבעת הצירים האלה נותן את הקשר בין המדדים הייצוגיים כך.
| מצב ה-page | Working Set | Private Working Set | Private Bytes | משפחת Virtual Bytes |
|---|---|---|---|---|
| פרטי ל-process, committed, resident ב-RAM | כלול | כלול | כלול | כלול |
| פרטי ל-process, committed, לא resident ב-RAM | אינו כלול | אינו כלול | כלול | כלול |
| shared page של DLL או mapped file, resident ב-RAM | כלול | בדרך כלל אינו כלול | בדרך כלל אינו כלול | כלול |
| Reserved אבל לא committed | אינו כלול | אינו כלול | אינו כלול | עשוי להיכלל |
| טווח כתובות שאינו בשימוש | אינו כלול | אינו כלול | אינו כלול | בדרך כלל אינו כלול |
flowchart TB
accTitle: מיפוי סוגי pages למדדי הזיכרון העיקריים
accDescr: מראה אילו מדדים כוללים pages פרטיים resident, pages פרטיים שאינם resident, shared pages resident, וטווחים reserved בלבד
privateResident["Private, committed, resident ב-RAM"]
privateNonresident["Private, committed, לא resident ב-RAM"]
sharedResident["shared page, resident ב-RAM"]
reservedOnly["Reserved, לא committed"]
workingSet["Working Set"]
privateWorkingSet["Private Working Set"]
privateBytes["Private Bytes"]
virtualBytes["משפחת Virtual Bytes"]
privateResident --> workingSet
privateResident --> privateWorkingSet
privateResident --> privateBytes
privateResident --> virtualBytes
privateNonresident --> privateBytes
privateNonresident --> virtualBytes
sharedResident --> workingSet
sharedResident --> virtualBytes
reservedOnly --> virtualBytes
איור 3: Working Set ו-Private Bytes סופרים קבוצות pages שונות, ולכן אין ביניהם יחס הכלה פשוט.
הנקודה החשובה כאן היא שWorking Set ו-Private Bytes אינם ביחס הכלה פשוט.
Private Bytes כולל pages שפרטיים ל-process אבל כרגע לא resident ב-RAM. Working Set, לעומת זאת, כולל shared pages — כמו קוד DLL וזיכרון משותף — ש-Private Bytes לא סופר בכלל. לכן, לפי ה-process ולפי הרגע, Working Set יכול להיות גדול מ-Private Bytes, או ההפך.
כמו כן, סיכום פשוט של ה-Working Set של כמה processes יכול לספור את אותו page פיזי — למשל DLL משותף — יותר מפעם אחת. “סכום ה-Working Set של כל process שווה ל-RAM שבשימוש” לא בהכרח מחזיק.
3. virtual address space — Reserve ו-Commit הם דברים שונים
3.1. virtual address אינה כתובת RAM פיזית
לכל process יש virtual address space פרטי משלו. pointer שהאפליקציה עובדת איתו לא מצביע ישירות על מיקום ב-RAM הפיזי; Windows משתמש ב-page tables כדי למפות virtual addresses ל-pages פיזיים או לנתונים בקובץ.7
כתוצאה מכך, גם במחשב עם 64GB RAM מותקן, ה-virtual address space ש-process נתון של 32-bit יכול להשתמש בו קטן בהרבה בדרך כלל. ולהפך, זה גם נורמלי ש-process של 64-bit יהיה בעל virtual address space גדול מה-RAM הפיזי.
3.2. Reserved אומר רק ש”הכתובת נתפסה”
MEM_RESERVE של VirtualAlloc שומר טווח virtual addresses רציף לשימוש עתידי. בשלב הזה אין physical storage שמשויך ל-pages, ואי אפשר לקרוא מהטווח או לכתוב אליו.45
למשל, גם אם database או runtime שומר טווח כתובות של 8GB לצמיחה עתידית, זה לבדו לא צורך 8GB של RAM או 8GB של Private Bytes.
3.3. Committed פירושו שיש backing כשיהיה צורך
MEM_COMMIT היא הפעולה שמעבירה virtual page למצב Committed וגורמת ל-Windows לספור את ה-backing הדרוש. האם קריאה, כתיבה או הרצה מותרות בפועל נקבע בנפרד לפי הגנת ה-page — PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE, PAGE_NOACCESS וכן הלאה — כך ש-Committed כשלעצמו לא אומר “ניתן לקריאה ולכתיבה”. ברגע ה-commit זה נספר ל-Commit Charge של המערכת, אבל ה-page הפיזי בפועל עשוי לא להיות מוקצה עד הגישה הראשונה. page שנוגעים בו בפעם הראשונה מאותחל באפסים, עובר demand-zero fault, ונכנס ל-Working Set.51
לכן, אף שקוראים לזה “הוקצה” בשני המקרים, בפועל יש שלושת השלבים הבאים.
flowchart TB
accTitle: שלושה שלבים מ-Reserve דרך Commit ל-residency ב-RAM
accDescr: מראה את הזרימה של שמירת virtual address, commit של ה-page, והקצאת page פיזי בגישה הראשונה עם כניסה ל-Working Set
reserve["MEM_RESERVE - שמירת טווח כתובות"]
reserve -.-> virtualMetric["משתקף במשפחת Virtual Bytes"]
reserve -->|MEM_COMMIT| committed["Committed - נגיש לפי הגנת ה-page"]
committed -.-> commitMetric["משתקף ב-Private Bytes / System Commit"]
committed -->|גישה ראשונה, demand-zero fault| resident["הוקצה page פיזי, resident ב-RAM"]
resident -.-> workingSetMetric["משתקף ב-Working Set"]
committed -.->|אם לא ניגשו מעולם| nonresident["Committed אבל לא resident"]
איור 4: Reserve, Commit והגישה הראשונה הם אירועים נפרדים, וכל אחד מזיז מדד אחר.
שלושת השלבים האלה מזיזים בנפרד, בהתאמה, את המספרים של משפחת Virtual Bytes, של Private Bytes ושל Working Set.
3.4. למה אפשר לקבל OutOfMemory גם כשיש RAM פנוי
האם הקצאת זיכרון מצליחה לא נקבע לפי RAM פנוי לבדו.
- ה-process כילה את ה-virtual address space שלו
- אין טווח כתובות פנוי בגודל הנדרש שהוא רציף
- ה-Commit Charge ברמת המערכת הגיע ל-Commit Limit
- ל-Job Object, ל-container, ל-runtime או ל-library יש מגבלה משלהם
- זה process של 32-bit
- ה-native heap מפורק (fragmented)
גם ב-Windows של 64-bit, ה-user-mode virtual address space של process של 32-bit הוא בדרך כלל 2GB אם IMAGE_FILE_LARGE_ADDRESS_AWARE אינו מוגדר. אפליקציית 32-bit עם הדגל הזה יכולה להשתמש עד 4GB ב-Windows של 64-bit.8
לכן “למחשב יש 20GB של RAM פנוי, ובכל זאת אפליקציית ה-32-bit נכשלת סביב 1.6GB” אינו סתירה. זו אולי כלל לא בעיית RAM, אלא fragmentation של ה-address space או פגיעה במגבלה קשיחה.
4. Working Set — pages שנמצאים כרגע ב-RAM
Working Set הוא קבוצת ה-pages, בתוך ה-virtual address space של process, שכרגע resident ב-RAM הפיזי.1
הקבוצה הזו היא תערובת של הבאים.
- ה-heap וה-stack של ה-process עצמו
- קוד EXE ו-DLL ונתונים לקריאה בלבד
- memory-mapped files
- shared memory
- pages שהפכו פרטיים לאותו process אחרי copy-on-write
- pages ש-runtime ו-libraries שונות נגעו בהם
4.1. Working Set שגדל אינו בהכרח אומר שהוקצה יותר
גישה בפעם הראשונה ל-page שכבר היה committed יכולה להגדיל את ה-Working Set לבדו בזמן ש-Private Bytes נשאר בלי שינוי. באותו אופן, כשקובץ גדול ממופה לזיכרון ונקרא ברצף, pages עם file backing נכנסים ל-Working Set בזמן ש-Private Bytes כמעט לא גדל.
ולהפך, כש-Windows עושה Trim ל-Working Set בתגובה ללחץ זיכרון, ה-Working Set לבדו מצטמק בזמן שהאפליקציה מחזיקה לוגית את אותו זיכרון. נגיעה חוזרת אחר כך מחזירה אותו דרך Page Fault.
לכן ירידה ב-Working Set לא בהכרח אומרת “האפליקציה שחררה”, ועלייה לא בהכרח אומרת “האפליקציה הקצתה מחדש”.
flowchart TB
accTitle: זרימה טיפוסית שבה רק ה-Working Set עולה ויורד
accDescr: אותו page committed נכנס ל-RAM בגישה הראשונה, מפסיק להיות resident ב-Trim, וחוזר בגישה חוזרת, בזמן ש-Private Bytes ממשיך להיספר
committed["אותו page committed"]
committed -->|גישה ראשונה| resident["resident ב-RAM"]
resident -->|Trim תחת לחץ זיכרון| nonresident["לא resident"]
nonresident -->|Page Fault בגישה חוזרת| resident
resident -.-> inWorkingSet["כלול ב-Working Set"]
nonresident -.-> outsideWorkingSet["אינו כלול ב-Working Set"]
committed -.-> privateBytes["נספר ב-Private Bytes כל עוד committed"]
איור 5: Working Set עולה ויורד עם ה-residency, אבל Private Bytes לא יורד כל עוד ה-Commit על אותו page נשאר.
4.2. Working Set כולל shared pages
אם 10 processes חולקים את דפי הקוד של אותו DLL, ה-page הזה יכול להופיע ב-Working Set של כל process, אף שבעותק אחד בלבד קיים ב-RAM הפיזי. סכום Working Set שחורג מה-RAM המותקן אינו מיד סימן לבעיה.
אם רוצים להתקרב ל-“ה-RAM ש-process זה לבדו תופס כרגע”, הסתכלו על Private Working Set. גם אז, זה אינו “כל הזיכרון שאותו process הקצה” — זה בדיוק ה-pages הפרטיים שכרגע resident.
4.3. הורדה בכפייה של ה-Working Set לא מתקנת memory leak
אפשר להשתמש ב-EmptyWorkingSet או ב-SetProcessWorkingSetSize כדי לפנות pages מ-Working Set של process. אבל זו לא פעולה שמשחררת Commit או משחררת הפניות ב-heap. שימוש ה-RAM הנראה יורד בזמן ש-Private Bytes נשאר בלי שינוי, והגישה הבאה יכולה להפעיל פרץ של Page Faults.9
אם הנתון ב-Task Manager מצטמק רק מיד אחרי שלוחצים על כפתור “הפחתת זיכרון”, ומיד עולה בחזרה כשחוזרים לעבוד, זה עשוי להיות רק Trim של Working Set ולא “שחרור” אמיתי.
5. Private Bytes — כמות ה-Commit הפרטית ל-process
Private Bytes הוא כמות ה-virtual memory ש-committed באופן בלעדי לאותו process. הוא מייצג Commit Charge שאי אפשר לשתף עם process אחר, ולא משנה אם הוא כרגע resident ב-RAM. ב-PROCESS_MEMORY_COUNTERS_EX של Microsoft, PrivateUsage מתאים לערך הזה.102
ל-Win32 API יש גם שדה בשם מבלבל, PagefileUsage, אבל התיעוד הנוכחי מגדיר אותו כ-“Commit Charge של אותו process” וקובע שזה אותו ערך כמו PrivateUsage. במילים אחרות, Private Bytes של 2GB אינו אומר “נכתבו 2GB ל-pagefile.sys”.2
Private Bytes מושפע בדרך כלל מהבאים.
- Commit של ה-native heap שמשמשים
HeapAlloc,malloc,newודומיהם - Private Data ש-committed ישירות עם
VirtualAlloc - האזור ה-committed של ה-GC heap של .NET
- החלק מ-thread stack ש-committed בפועל
- ה-Commit Charge לכל ה-view שנשמר כשממפים תצוגת copy-on-write (
FILE_MAP_COPY) - buffers פרטיים ש-libraries ו-SDK של התקנים מחזיקים בפנים
בתצוגת copy-on-write שנוצרת עם FILE_MAP_COPY, כל page יכול בסופו של דבר להפוך לפרטי, ולכן בזמן המיפוי Windows שומר Commit Charge מספיק כדי לגבות את כל ה-view ב-pagefile. בגלל זה System Commit ו-Commit Charge של ה-process (Private Bytes) יכולים לעלות בגודל כל ה-view עוד לפני שכתיבה כלשהי יוצרת בפועל עותק פרטי.11
5.1. למה Private Bytes לא יורד אחרי free או GC
גם כשהזיכרון “משוחרר” מנקודת המבט של האפליקציה, ה-runtime או ה-heap allocator עשויים שלא לעשות Decommit של האזור בחזרה ל-OS, אלא להחזיק אותו לשימוש חוזר עתידי. במקרה כזה Private Bytes נשאר גבוה אף שהאזור ניתן לשימוש חוזר בפנים בתוך האפליקציה.
הוא יכול גם להישאר גבוה מסיבות כמו שרק חלק מאזור גדול עדיין חי, fragmentation, או cache או pool שהתחממו עד התקרה.
לכן Private Bytes גבוה לבדו לא מוכיח memory leak. מה שצריך לבדוק הוא השוואה לאורך זמן:
- חזרו על אותו עיבוד אותו מספר פעמים
- המתינו אותו זמן אחרי העיבוד
- בדקו אם Private Bytes חוזר לאותה רמה, או מתייצב על ערך קבוע
- השתמשו ב-VMMap או ב-heap dump כדי לבדוק איזה אזור או סוג גדל
flowchart TB
accTitle: למה Private Bytes לא יורד אחרי free או GC
accDescr: Private Bytes משתנה אחרת בהתאם לשאלה אם ה-allocator מחזיר ל-OS אזור שהאפליקציה כבר לא צריכה, או מחזיק אותו לשימוש חוזר
release["האפליקציה משחררת אזור דרך free / GC"]
release --> decision{"האם ה-allocator מחזיר אותו ל-OS"}
decision -->|Decommit / Release| returned["Commit Charge יורד"]
returned --> lower["Private Bytes יורד"]
decision -->|מחזיק לשימוש חוזר| retained["האזור נשאר committed"]
retained --> high["Private Bytes נשאר גבוה"]
retained --> reasons["pools, caches, fragmentation"]
איור 6: אזור שהפך ניתן לשימוש חוזר בתוך האפליקציה אינו אותו דבר כמו החזרת ה-Commit שלו ל-OS.
5.2. תבנית מועמדת חזקה ל-memory leak
עלייה כמו הבאה, שבה ה-baseline עולה במדרגות עם כל סבב עומס, ראויה לתשומת לב.
Private Bytes
^
| ________
| ______|
| ______|
|_____|
+----------------------------> חזרות על אותו עיבוד
עם זאת, גם צורת מדרגות יכולה להיות רק כמה סבבי צמיחה מ-JIT בפעם הראשונה, גופנים, image decoders, connection pools או cache warmup, ואחריהם היא מתייצבת. מה שחשוב אינו שהיא גדלה, אלא שהיא נכשלת להתכנס ל-steady state.
6. System Commit — מה באמת “Committed X/Y”
הנתון “Committed X/Y” בלשונית [Performance] ← [Memory] של Task Manager הוא מדד ברמת המערכת.
- X: System Commit Charge — ה-committed memory ש-Windows סופר כרגע כ-Commit Charge בכל המערכת
- Y: System Commit Limit — תקרת ה-Commit שהמערכת יכולה לתמוך בה
Commit Limit נקבע בערך לפי RAM פיזי ועוד סך כל ה-pagefiles. בלי pagefile, הוא יוצא קצת קטן מה-RAM המותקן.36
flowchart TB
accTitle: הקשר בין System Commit Charge ל-Commit Limit
accDescr: Commit פרטי לכל process, של shared sections ושל ה-kernel מרכיבים את הערך הנוכחי X, בעוד RAM פיזי ו-pagefile תומכים בתקרה Y
processCommit["Private Commit של כל process"] --> charge["System Commit Charge - X"]
sharedCommit["Commit של shared sections עם pagefile backing"] --> charge
kernelCommit["Commit של ה-kernel"] --> charge
physicalRam["RAM פיזי"] --> limit["System Commit Limit - Y"]
pageFiles["pagefile"] --> limit
charge -->|X לא יכול לעבור את Y| limit
איור 7: X הוא כמות ה-Commit הנוכחית ו-Y הוא התקרה שיכולה לתמוך בה — זו לא תצוגה של שימוש ב-pagefile.
System Commit Charge כולל לא רק את סכום ה-Private Bytes של כל process, אלא גם את ה-Commit של shared sections שמגובים ב-pagefile ואת ה-Commit שה-kernel צורך. לכן סכום ה-Private Bytes לפי process לבדו לא יכול להסביר את X במלואו.
6.1. Commit Charge אינו שימוש ב-pagefile
קחו מערכת עם 16GB RAM, pagefile של 16GB, ו-Committed ב-20/31GB.
אותם 20GB לא אומרים “נכתבו 20GB ל-pagefile”. זו הכמות הכוללת ש-Windows סופר כ-Commit Charge — כלומר, ל-pages פרטיים הניתנים לכתיבה וכדומה, ייש backing של RAM או של pagefile כשיהיה צורך.
באותו רגע יכול להתקיים ערבוב של המצבים הבאים:
- רובו resident ב-RAM
- חלקו פונה ל-pagefile
- חלקו committed אבל עדיין לא הייתה לו גישה ראשונה
- חלקו נצרך כ-Commit מצד ה-kernel
אם רוצים לראות שימוש בפועל ב-pagefile, בדקו Paging File(*)\% Usage בנפרד מ-Commit. גם החומר של Microsoft עצמה מסביר ששימוש גבוה ב-pagefile לבדו לא בהכרח מעיד על בעיית ביצועים, ושצריך לשפוט אותו יחד עם הגעה ל-Commit Limit, עם Modified Page List ועם paging I/O בפועל.6
6.2. מה קורה כשמתקרבים ל-Commit Limit
כש-System Commit Charge מגיע ל-Commit Limit, אי אפשר לגבות בקשות commit חדשות. זה מוביל לכישלונות הקצאת זיכרון של processes, לקריסות אפליקציות ולמערכת שלא מגיבה.3
כאן X/Y של Commit חשוב יותר מ-“RAM פנוי”. גם אם עושים Trim ל-Working Set כדי לפנות RAM, הגעה ל-Commit Limit לא נפתרת אלא אם ה-Commit Charge עצמו יורד.
6.3. שלושת התפקידים של ה-pagefile
ה-pagefile משרת בעיקר את התפקידים הבאים.
- הרחבת ה-Commit Limit
- לאפשר לפנות מ-RAM pages שסומנו Modified ונמצאים בשימוש נדיר
- לגבות את ה-crash dump של המערכת, בהתאם להגדרה
השבתת pagefile אינה מקרה פשוט של “I/O לדיסק תמיד יורד והדברים נעשים מהירים יותר”. אם כבר, היא מורידה את ה-Commit Limit, מגדילה את הסיכוי ש-pages שסומנו Modified אבל כרגע אינם נחוצים יישארו ב-RAM, ויכולה להפוך לבלתי אפשרי לאסוף את ה-dump שצריך כשקריסה מתרחשת.36
את גודל ה-pagefile המתאים אי אפשר להחליט לפי ה-RAM המותקן לבדו. Microsoft עצמה מסבירה שאי אפשר להכליל, כי שיא ה-System Commit Charge וסוג ה-crash dump הנדרש שונים ממערכת למערכת.6
7. פירוט ה-RAM הפיזי — אל תשפטו מ-Available נמוך לבדו
RAM פיזי אינו משמש רק את ה-Working Set של user processes.
- ה-Working Set של כל process
- system file cache
- page lists כמו Standby, Modified, Free ו-Zeroed
- Paged Pool / Nonpaged Pool של ה-kernel
- זיכרון שמחזיקים device drivers
- מחסן דחיסת הזיכרון
- אזורים משותפים עם ה-GPU והתקנים אחרים או שמורים להם
- זיכרון ששמור לחומרה
7.1. Available כולל גם cache שניתן לשימוש חוזר
Available MBytes של Windows אינו פשוט RAM שאינו בשימוש כלל. זה מדד שכולל, לצד Free ו-Zeroed, גם Standby pages שאפשר להשתמש בהם מחדש אם צריך.12
- Free: pages שאינם מוקצים כרגע לאף מטרה
- Zeroed: pages שאופסו כך שאפשר למסור אותם בבטחה ל-process אחר
- Standby: pages שעזבו Working Set אבל התוכן שלהם עדיין ב-cache ב-RAM
- Modified: pages שהתוכן שלהם השתנה ושצריך לכתוב אותם ל-backing המתאים לפני שימוש חוזר
flowchart TB
accTitle: תנועה בין ה-Working Set ל-page lists
accDescr: מראה pages שלא השתנו שיוצאים ל-Standby ו-pages שסומנו Modified שיוצאים ל-Modified, ואז גישה חוזרת, כתיבה ושימוש חוזר
workingSet["Working Set - בשימוש"]
workingSet -->|הוסר page שלא השתנה| standby["Standby - מועמד לשימוש חוזר עם תוכן שנשמר"]
workingSet -->|הוסר page שסומן Modified| modified["Modified - ממתין לכתיבה"]
modified -->|הכתיבה הושלמה| standby
standby -->|גישה חוזרת| workingSet
standby -->|שימוש חוזר למטרה אחרת| reused["הוקצה למטרה אחרת"]
free["Free - אינו בשימוש"] -->|אופס| zeroed["Zeroed - זמין להקצאה חדשה"]
zeroed -->|גישה אחרי הקצאה| workingSet
standby -.-> available["כלול ב-Available"]
free -.-> available
zeroed -.-> available
איור 8: Available כולל לא רק זיכרון פנוי לגמרי אלא גם Standby, שאפשר להשתמש בו מחדש אם צריך.
“לזרוק את כל ה-cache כדי להגדיל RAM פנוי” אינו תמיד ניצחון. אם הנתונים שצריך עדיין יושבים ב-Standby, גישה חוזרת יכולה להחזיר אותם ל-Working Set במהירות בלי לקרוא מהדיסק.
לכן גם אם Free נמוך ב-Task Manager, אם Available מרווח ו-hard Page Faults או המתנות דיסק לא גורמות לבעיות, Windows פשוט עשוי להשתמש ב-RAM ביעילות כ-cache.
7.2. כשה-RAM מצטמק בלי process גדול
אין זה נדיר שצריכת זיכרון תישאר בלתי מוסברת גם אחרי שמחברים את ה-Private Working Set של כל process.
- file cache ו-memory-mapped files
- Nonpaged Pool / Paged Pool
- pages שנעלו על ידי driver
- shared pages
- דחיסת זיכרון
- allocations שקשורות לווירטואליזציה או ל-GPU
במקרה כזה, במקום להמשיך לבהות ברשימת ה-processes, בדקו Use Counts, Processes, Priority Summary ו-File Summary ב-RAMMap של Sysinternals. RAMMap הוא הכלי הרשמי לפירוק זיכרון פיזי לפי מטרה, page list וקובץ.13
אם רק Nonpaged Pool ממשיך לגדול, זו הנקודה שבה יש לחשוד ב-memory leak מצד driver או kernel, ולא ב-Private Bytes של אפליקציה ב-user mode.
8. Page Fault — ספירה גבוהה אינה חריגה כשלעצמה
Page Fault מתרחש כש-process ניגש ל-page שאינו כרגע ב-Working Set שלו. למרות המילה Fault בשם, זו לא כשל חריג — זה המנגנון הרגיל שמניע virtual memory.1
8.1. Soft page fault
אלה נפתרים בלי לקרוא מהדיסק.
- ה-page עדיין ב-Standby או ב-Transition
- אותו shared page כבר נמצא ב-Working Set של process אחר
- ניגשים בפעם הראשונה ל-page committed ומוקצה page של אפסים
- ה-prefetch של מנהל הזיכרון כבר הכניס אותו ל-RAM
לכן \Memory\Page Faults/sec גדול אינו בהכרח אומר שמתרחש I/O לדיסק או latency.
8.2. Hard page fault
אלה דורשים לקרוא תוכן מ-backing store בדיסק. המקור אינו מוגבל ל-pagefile.
- קוד ונתונים ב-
.exeאו ב-.dll - memory-mapped file
- pagefile
flowchart TB
accTitle: הענף בין soft page fault ל-hard page fault
accDescr: בגישה ל-page שאינו ב-Working Set, מטפלים כ-soft page fault אם אין צורך ב-storage I/O, או כ-hard page fault אם יש
access["גישה ל-page שאינו ב-Working Set"] --> storageIo{"האם נדרש storage I/O"}
storageIo -->|לא - Standby, shared, demand-zero וכו| soft["Soft page fault"]
soft --> resident["נכנס ל-Working Set בלי קריאת דיסק"]
storageIo -->|כן| hard["Hard page fault"]
hard --> source{"מאיפה קוראים"}
source --> image["EXE / DLL"]
source --> mapped["memory-mapped file"]
source --> pagefile["pagefile"]
image --> loaded["נכנס ל-Working Set אחרי טעינה"]
mapped --> loaded
pagefile --> loaded
איור 9: השם Page Fault לבדו לא יכול לומר אם התרחש I/O לדיסק.
Microsoft מונה את \Memory\Pages/sec, \Memory\Page Reads/sec ו-\Memory\Pages Input/sec בין המונים למדידת hard fault. מכיוון שגובהם אינו בהכרח אומר שחסר זיכרון, קשרו אותם ל-Available MBytes, ל-disk latency ולזמן תגובה בפועל.6
8.3. אל תציבו סף כולל אחד
ערך קבוע כמו “כל דבר מעל 1000 Page Faults/sec הוא חריג” משנה משמעות בהתאם ל-storage, לגודל page, ל-workload ולמקומיות הגישה.
בפועל, סדרו את הבאים על אותו ציר זמן.
Memory\Available MBytesMemory\Pages Input/secMemory\Page Reads/sec- Read latency / Queue בדיסק היעד
- Working Set ו-Private Bytes של ה-process היעד
- זמן העיבוד של האפליקציה, timeouts ותגובתיות UI
אם Available יורד באותו זמן שהעומס עולה, Pages Input/sec והמתנת דיסק עולים, וגם זמן העיבוד מחמיר, יש בסיס לחשוד ב-paging שנגרם מלחץ RAM פיזי.
9. איזה מסך או כלי לבדוק בשביל מה
| מה רוצים לדעת | המדד לבדוק תחילה | הכלים העיקריים |
|---|---|---|
| הכמות ש-process היעד מחזיק כרגע ב-RAM | Working Set | Task Manager, Process Explorer, Get-Process |
| החלק הפרטי מתוך זה — RAM פרטי ל-process | Private Working Set / Working Set - Private | עמודות Details ב-Task Manager, Process Explorer, PerfMon |
| כמות ה-Commit הפרטית ל-process היעד | Private Bytes / Commit Size | Process Explorer, PerfMon, VMMap, Get-Process |
| טווח ה-virtual addresses של ה-process | Virtual Bytes / Size | Process Explorer, VMMap, Get-Process |
| מרווח ה-Commit הכולל של המערכת | Committed Bytes / Commit Limit | Task Manager [Performance], PerfMon |
| מרווח השימוש החוזר של RAM פיזי | Available MBytes | Task Manager, PerfMon |
| פירוט Standby, Modified ו-file cache | page list / פירוט לפי מטרה | RAMMap |
| מה גדל בתוך Private Bytes | Heap / Private Data / Managed Heap וכו | VMMap, WinDbg, dump ייעודי ל-runtime |
| paging שמערב דיסק | Pages Input/sec, Page Reads/sec, disk latency | PerfMon, WPR/WPA |
flowchart TB
accTitle: בחירת כלי חקירת זיכרון של Windows
accDescr: הכלי לשימוש תלוי בשאלה אם היעד הוא process אחד או כל המערכת, נקודה אחת בזמן או time series, ואם צריך לעקוב אחרי החזקה בתוך runtime
question["מה רוצים לבודד"]
question --> processScope{"האם היעד הוא process אחד"}
processScope -->|כן| processTime{"נקודה אחת או time series"}
processTime -->|פירוט של נקודה אחת| vmmap["VMMap"]
processTime -->|time series| perfmon["PerfMon / PowerShell"]
processScope -->|כל המערכת| systemView{"פירוט RAM פיזי או ציר זמן"}
systemView -->|פירוט RAM פיזי| rammap["RAMMap"]
systemView -->|ציר זמן כולל CPU, I/O והמתנות| wpa["WPR / WPA"]
question --> runtime{"האם צריך לעקוב אחרי החזקה בתוך runtime"}
runtime -->|heap של .NET| dotnet["dotnet-dump / PerfView"]
runtime -->|native heap| native["WinDbg / Application Verifier"]
איור 10: החלטה תחילה על ההיקף ועל ציר הזמן מאפשרת לבחור בדיוק את הכלי שצריך, לא יותר ולא פחות.
9.1. Task Manager
ב-Task Manager, הסתכלו על המסכים בנפרד.
- [Processes] או [Details]: משפחת Working Set ומשפחת Commit Size של processes בודדים
- [Performance] ← [Memory]: In use, Available, Committed, Cached, Paged pool, Non-paged pool ברמת המערכת
אל תשפטו מעמודה בשם Memory לבדה — לחצו לחיצה ימנית על כותרות העמודות בלשונית [Details] והוסיפו את העמודות שצריך, כמו Working Set, Peak Working Set ו-Commit Size. שמות העמודות משתנים מעט לפי גרסת Windows ושפת התצוגה, לכן אמתו מה עמודה באמת אומרת לפני שרושמים אותה.
9.2. רישום time series עם PowerShell
אם יודעים את ה-PID של ה-process, אפשר לרשום יחד את מגמות Working Set, Private Bytes ו-Virtual Bytes עם Get-Process.
param(
[Parameter(Mandatory)]
[int]$ProcessId,
[int]$IntervalSeconds = 5,
[int]$SampleCount = 60
)
$samples = for ($i = 0; $i -lt $SampleCount; $i++) {
$process = Get-Process -Id $ProcessId -ErrorAction Stop
[pscustomobject]@{
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
ProcessId = $process.Id
WorkingSetMB = [math]::Round($process.WorkingSet64 / 1MB, 1)
PrivateBytesMB = [math]::Round($process.PrivateMemorySize64 / 1MB, 1)
VirtualBytesMB = [math]::Round($process.VirtualMemorySize64 / 1MB, 1)
Handles = $process.HandleCount
Threads = $process.Threads.Count
}
Start-Sleep -Seconds $IntervalSeconds
}
$samples | Format-Table -AutoSize
$samples | Export-Csv .\memory-samples.csv -NoTypeInformation -Encoding utf8
Process.WorkingSet64 של .NET מתאים ל-Working Set, PrivateMemorySize64 ל-Private Bytes, ו-VirtualMemorySize64 ל-Virtual Bytes.141516
לאפליקציה עם כמה instances, עקבו לפי PID ולא לפי שם. לניטור ארוך טווח שבו הפעלה מחדש משנה את ה-PID, תכננו את האיסוף כך שיירשם זמן ההתחלה, שם ה-service וכדומה, כדי שהיעד לעולם לא יוחלף בטעות.
9.3. לשים מערכת ו-process על אותו ציר זמן עם PerfMon
רישום לפחות של הבאים יחד מקל מאוד על הבידוד.
\Process(<יעד>)\ID Process
\Process(<יעד>)\Working Set
\Process(<יעד>)\Working Set - Private
\Process(<יעד>)\Private Bytes
\Process(<יעד>)\Virtual Bytes
\Memory\Available MBytes
\Memory\Committed Bytes
\Memory\Commit Limit
\Memory\Pages Input/sec
\Memory\Page Reads/sec
\Memory\Pool Nonpaged Bytes
\Memory\Pool Paged Bytes
כשכמה processes חולקים את אותו שם, או שמתרחשת הפעלה מחדש במהלך הניטור, שם ה-instance לבדו — Process(name) או Process(name#N) — לא יכול לנעוץ את היעד. רשמו גם ID Process לכל דגימה, ואמצו רק את ה-instance שערכו תואם ל-PID שעוקבים אחריו. כשחוצים הפעלה מחדש שמשנה את ה-PID, רשמו בנפרד גם את זמן המעבר.
שמות מוני הביצועים של Windows יכולים להיות מתורגמים בהתאם לשפת התצוגה. אם ציון השם האנגלי ישירות ב-PowerShell לא מוצא אותו, הוסיפו את המונה דרך ממשק PerfMon הגרפי, או בדקו את השמות בסביבה המקומית עם Get-Counter -ListSet *.
9.4. אל תבלבלו בין התפקידים של VMMap ושל RAMMap
- VMMap: מפרק את ה-virtual memory ואת ה-Working Set של process אחד ל-Heap, Image, Mapped File, Private Data, Managed Heap וכדומה
- RAMMap: מפרק את ה-RAM הפיזי של כל המערכת לפי מטרה, page list, process וקובץ
“מה גרם ל-Private Bytes של ה-process הזה לגדול” הוא עבודה ל-VMMap; “למה משמש ה-RAM שרשימת ה-processes לא מסבירה” הוא עבודה ל-RAMMap.1713
10. קריאת תסמינים מצירופי מספרים
| התבנית שנצפתה | ההשערה הראשונה | מה לבדוק אחר כך |
|---|---|---|
| Working Set עולה, Private Bytes יציב | גישה ראשונה ל-pages קיימים, DLL משותף, mapped file, file cache | Image / Mapped File של VMMap, Pages Input/sec |
| Private Bytes עולה, Working Set יציב | Commit פרטי גדל אבל אינו resident או עבר Trim | Heap / Private Data / Managed Heap של VMMap |
| שניהם עולים מיד אחרי ההפעלה, ואז מתיישרים | JIT, cache, pool, warmup של אתחול | האם זה גדל שוב תחת אותו עומס נוסף |
| ה-baseline של Private Bytes עולה עם כל סבב עומס | memory leak, cache בלי תקרה, או allocator שמחזיק זיכרון אחרי שחרור | צילומי VMMap לפני ואחרי, heap dump |
| רק Working Set צונח בפתאומיות וחוזר עם פעילות | ה-OS או האפליקציה עשו Trim ל-Working Set | Private Bytes, Pages Input/sec, זמן תגובה |
| X ב-Committed X/Y מתקרב ל-Y | לחץ Commit ברמת המערכת | הצרכנים הגדולים של Private Bytes, Paged/Nonpaged Pool, הגדרות pagefile |
| Available נמוך, Pages Input/sec ו-disk latency גבוהים | לחץ RAM פיזי ו-hard paging | הצרכנים הגדולים של Working Set, RAMMap, מתאם עומס |
| שימוש ב-RAM גבוה אבל אין process גדול | cache, shared pages, kernel pools, drivers, דחיסה וכו | RAMMap, Pool Nonpaged/Paged Bytes |
| יש RAM פנוי, ובכל זאת רק אפליקציית ה-32-bit נכשלת | תקרת virtual address space או fragmentation | Free/Reserved של VMMap, הגדרת LAA של קובץ ההרצה |
| Private Bytes גבוה אבל אינו גדל בחזרה על העיבוד | pool או cache שאולי מחזיק high-water mark | המגבלה שלו, התנהגות שימוש חוזר, יציבות אחרי השיא |
הדבר החשוב ביותר בטבלה הזו הוא לקרוא אותה בשילוב, לא מערך בודד.
11. הליך מעשי לחקירת memory leak
11.1. קבעו תחילה את תנאי השחזור ואת נקודת היציבות
“זה גדל במשך כמה ימים” לבדו אינו ניתן להשוואה.
- כמה מ-warmup שאחרי ההפעלה לכלול
- ממה מורכב מחזור פעולה אחד
- כמה שניות להמתין אחרי מחזור אחד
- כמה סבבים לוקח להגיע לתקרת ה-cache
- האם אפשר להשתמש באותו קלט לגרסה הבריאה ולגרסה הבעייתית
החליטו את כל אלה.
11.2. רשמו את ה-process ואת המערכת יחד
לכל הפחות, שמרו את הבאים בחותמות זמן זהות.
- Working Set של היעד
- Private Bytes של היעד
- Virtual Bytes של היעד
- Committed Bytes / Commit Limit של המערכת
- Available MBytes
- Pages Input/sec
- מספר handles, מספר threads
- מספר פעולות או פריטים שעובדו
אם ה-Private Bytes של ה-process יציב בזמן ש-Commit של המערכת ממשיך לגדול, צריך להרחיב את ההיקף ל-processes אחרים, ל-kernel, ל-drivers ול-shared sections.
11.3. החליטו תחילה איזה “ממד” גדל
- Working Set לבדו: pages resident, משותפים או שמקורם בקובץ, Trim וטעינה מחדש
- Private Bytes: Commit פרטי ל-process
- Virtual Bytes לבדו: Reserve, mapping, fragmentation של address space
- System Commit לבדו: כולל processes אחרים וצד ה-kernel
- Nonpaged Pool: צד driver/kernel
- Handles / GDI / USER: resource leaks שאינם זיכרון
לדלג על הסדר הזה ולקפוץ ישר ללקיחת dump פירושו לקרוא הר של מידע בזמן שמכוונים לדבר הלא נכון.
11.4. עברו לפירוט
- process native: VMMap, WinDbg, Application Verifier, מעקב heap
- .NET:
dotnet-counters,dotnet-gcdump,dotnet-dump, PerfView - ברמת המערכת: RAMMap, PerfMon, WPR/WPA
- kernel pool: PoolMon, WinDbg
VMMap מציג את ה-virtual memory ה-committed של process, ואת ה-Working Set שהוקצה לכל חלק ממנו, מפורק לפי סוג. עד כמה אפשר לצמצם את הגידול ב-Private Bytes ל-Heap, Private Data, Managed Heap או Mapped File משנה מאוד את עלות החקירה שאחרי.17
11.5. אחרי תיקון, השוו את המגמה באותם תנאים
אין די בכך שערך השיא שונה לפני התיקון ואחריו. אם ערך ההתחלה שונה, ההשוואה יכולה להתהפך בקלות.
- אותו מצב הפעלה
- אותו קלט
- אותו מספר פעולות
- אותו זמן המתנה
- אותו מרווח דגימה
— והשוו את ערך ה-baseline ואת המגמה אחרי כל מחזור. הוכחת תיקון memory leak אינה “המקסימום קטן יותר”, אלא שהגידול מתכנס עכשיו גם כשחוזרים על אותו עומס.
12. ניסוח מחדש של טעויות נפוצות
טעות נפוצה 1: הזיכרון ב-Task Manager שווה לכמות הכוללת שאפליקציה הקצתה
מה שזה באמת: בדקו איזו עמודה זו. למשפחת Working Set זו הכמות שכרגע resident ב-RAM; למשפחת Commit Size זה ה-Commit הפרטי לאותו process.
טעות נפוצה 2: Private Bytes שווה לבתים ב-pagefile
מה שזה באמת: Private Bytes הוא Commit Charge פרטי. זו כמות חשבונאית שכוללת גם pages שנמצאים כרגע ב-RAM וגם pages שיגובו ב-pagefile אם וכאשר יהיה צורך.
טעות נפוצה 3: Commit X/Y שווה לשימוש ב-pagefile / לקיבולת pagefile
מה שזה באמת: X הוא Commit Charge ברמת המערכת, ו-Y הוא Commit Limit. ה-pagefile מרחיב את Y, אבל X לא מתרגם ישירות לשימוש בדיסק.
טעות נפוצה 4: Page Faults/sec גבוה אומר שמחליפים לדיסק
מה שזה באמת: זה כולל גם soft fault. בדקו Pages Input/sec, Page Reads/sec ו-disk latency כדי לראות אם I/O לדיסק מעורב בפועל.
טעות נפוצה 5: RAM פנוי נמוך אומר שחסר זיכרון
מה שזה באמת: הסתכלו על Available, Standby, hard paging וזמן תגובה. למלא RAM ב-cache שניתן לשימוש חוזר זה נורמלי.
טעות נפוצה 6: הקטנת ה-Working Set אומרת שתוקן memory leak
מה שזה באמת: אולי רק פיניתם pages מה-RAM. בדקו אם Private Bytes ומה שמוחזק בתוך ה-heap באמת ירדו.
טעות נפוצה 7: עלייה ב-Private Bytes מאשרת memory leak
מה שזה באמת: אפשר לשפוט זאת רק אחרי שבדקתם אם זה מתכנס כשחוזרים על אותו workload, איזה סוג זיכרון גדל, והאם זה cache שניתן לשחרור.
13. סיכום
- Memory Usage של Windows אינו מספר אחד. חשבו בנפרד על address space, Commit, residency ב-RAM ואפשרות שיתוף.
- Working Set הוא ה-pages שנמצאים כרגע ב-RAM, כולל גם Private וגם Shared. Private Working Set הוא ה-pages הפרטיים ל-process ש-resident בתוך זה.
- Private Bytes הוא Commit Charge פרטי ל-process; הוא אינו הכמות שנמצאת כרגע ב-RAM ואינו הכמות שנכתבה בפועל ל-pagefile.
- Committed X/Y הוא Commit Charge / Commit Limit ברמת המערכת. ה-pagefile תומך בעיקר ב-Commit Limit, בפינוי pages שסומנו Modified וב-crash dump.
- virtual address Reserved, page Committed, ו-page שנגעו בו בפועל ונכנס ל-Working Set הם שלבים נפרדים.
- Page Fault הוא פעולה רגילה, ו-soft fault אינו קורא מהדיסק. גם hard fault יכולים להתרחש לא רק מ-pagefile אלא מ-EXE, DLL או mapped file.
- memory leak מוכח לא לפי הגודל בנקודת זמן אחת, אלא לפי ערך ה-baseline והמגמה אחרי אותו עומס, יחד עם הפירוט.
- הנתיב הבסיסי: VMMap לפירוט של process בודד, RAMMap ל-RAM פיזי ברמת המערכת, PerfMon ל-time series, וכלי dump ייעודי למה שקורה בתוך runtime.
בפעם הבאה שתשימו לב ב-Task Manager ש-“הזיכרון גדל”, התחילו בשאלה הזו.
מה שגדל הוא Working Set, Private Bytes, Virtual Bytes או System Commit?
השאלה הזו לבדה הופכת את נקודת הכניסה לחקירה למדויקת בהרבה.
מאמרים קשורים
- להבחין בין עיכוב GC ל-memory leak ב-.NET — הליך מעשי לצפייה, להשוואה ולהוכחה של גידול בזיכרון
- Process Explorer / Handle / VMMap בפועל — לרדוף אחרי hangs, leaks ו-“הקובץ בשימוש” ממצב הרגע
- מלכודות shared memory ושיטות עבודה מומלצות בפועל
- מעמקי ה-I/O של Windows (חלק 4) — Cache Manager: מתי WriteFile שלכם באמת מגיע לדיסק?
- חקירת קריסה בהפעלה ממושכת של מצלמה תעשייתית — פרק handle leak
תחומי ייעוץ קשורים
KomuraSoft LLC מטפלת בחקירות סיבת שורש שמשלבות PerfMon, VMMap, RAMMap, WinDbg וכלי אבחון של .NET לגידול זיכרון של אפליקציות Windows, להידרדרות ביצועים אחרי הפעלה ממושכת, ל-OutOfMemory ב-processes של 32-bit, ולמחסור בזיכרון שמתרחש רק בסביבת הלקוח. אנחנו לא עוצרים ב-“הזיכרון גבוה” — אנחנו מבודדים איזה אזור גדל, דרך איזו פעולה, למה, ומאין מפנים אליו או מחזיקים אותו.
מקורות
-
Microsoft Learn, Working Set. על כך ש-Working Set של process הוא קבוצת ה-pages שכרגע resident בזיכרון פיזי, כולל shared pages; על ההבדל בין soft page fault ל-hard page fault; על Transition pages; ועל הסרת pages מה-Working Set. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PROCESS_MEMORY_COUNTERS_EX2 structure. על ההגדרות של WorkingSetSize, PrivateWorkingSetSize, PrivateUsage ו-SharedCommitUsage, ועל כך שגם PagefileUsage וגם PrivateUsage מייצגים את ה-Commit Charge של ה-process. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Introduction to page files. על כך שה-pagefile תומך בפינוי pages שסומנו Modified, ב-crash dump של המערכת ובהרחבת System Commit Limit; על ההגדרות של System Commit Charge ו-Commit Limit; ועל אופן המדידה דרך Task Manager ומוני ביצועים. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Page State. על מצבי Free, Reserved ו-Committed של virtual page, ועל כך של-pages Reserved אין physical storage משויך והם אינם נגישים. ↩ ↩2
-
Microsoft Learn, VirtualAlloc function. על ההבדל בין MEM_RESERVE ל-MEM_COMMIT; על כך ש-commit מחויב כנגד הזיכרון הכולל של המערכת וה-pagefile; ועל כך שה-page הפיזי בפועל לפעמים אינו מוקצה עד הגישה הראשונה. ↩ ↩2 ↩3
-
Microsoft Learn, How to determine the appropriate page file size for 64-bit versions of Windows. על כך שגודל ה-pagefile תלוי בשיא Commit Charge ובדרישות crash dump; על כך ש-hard page fault קורא לא רק מ-pagefile אלא גם מ-EXE, DLL ו-memory-mapped files; ועל מוני הביצועים הקשורים. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Virtual Address Space. על כך שלכל process יש virtual address space עצמאי ו-page table משלו, ו-virtual address אינה כתובת פיזית כשלעצמה. ↩
-
Microsoft Learn, Memory Limits for Windows and Windows Server Releases. על כך שה-user-mode virtual address space של process של 32-bit הוא בדרך כלל 2GB, והופך ל-2GB או ל-4GB ב-Windows של 64-bit בהתאם ל-IMAGE_FILE_LARGE_ADDRESS_AWARE. ↩
-
Microsoft Learn, SetProcessWorkingSetSize function. על כך שערכי המינימום והמקסימום של Working Set אינם מבטיחים residency; על היכולת לרוקן Working Set; ועל כך שהגדרות או פעולות מופרזות יכולות להרע את ביצועי המערכת. ↩
-
Microsoft Learn, Memory Performance Information. על ההתאמה בין מוני הביצועים של Windows, ממשקי ניהול הזיכרון ותצוגת Task Manager, כולל Working Set / Working Set - Private / Private Bytes של אובייקט Process, ו-Committed Bytes / Commit Limit של אובייקט System. ↩
-
Microsoft Learn, MapViewOfFile function. על כך ש-
FILE_MAP_COPYהופך כל page ל-copy-on-write פוטנציאלי, כך ש-Commit Charge לכל ה-view נשמר כדי להיות מגובה ב-pagefile בזמן המיפוי. ↩ -
Microsoft Learn, Understanding Node Metrics and Properties in HPC Cluster Manager. על כך ש-Available Physical Memory מחושב כסכום רשימות Zeroed, Free ו-Standby, ועל משמעות כל אחת מ-page lists האלה. ↩
-
Microsoft Sysinternals, RAMMap. על ניתוח שימוש הזיכרון הפיזי של Windows לפי מטרה, page list, process, עדיפות, page פיזי וקובץ. ↩ ↩2
-
Microsoft Learn, Process.WorkingSet64 Property. על כך ש-
WorkingSet64מחזיר את ה-Working Set של ה-process בבתים, בהתאמה למונה הביצועים Working Set של אובייקט Process. ↩ -
Microsoft Learn, Process.PrivateMemorySize64 Property. על כך ש-
PrivateMemorySize64מחזיר את הזיכרון הפרטי ל-process שאי אפשר לשתף עם processes אחרים, בהתאמה למונה הביצועים Private Bytes. ↩ -
Microsoft Learn, Process.VirtualMemorySize64 Property. על כך ש-
VirtualMemorySize64מחזיר את כמות ה-virtual memory שהוקצתה ל-process, בהתאמה למונה הביצועים Virtual Bytes. ↩ -
Microsoft Sysinternals, VMMap. על פירוק ה-virtual memory ה-committed של process לפי סוג, ועל הצגת הזיכרון הפיזי (Working Set) שהוקצה לכל אחד, יחד עם מפת זיכרון מפורטת. ↩ ↩2
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
מנגנון הזיכרון של Windows (חלק 2) — מחזור החיים של דף פיזי: חמש רשימות והאמת על ה-pagefile
המאמר מחבר את ה-PFN database, Standby, Modified, דחיסת זיכרון וה-pagefile, ומסביר לאן עובר דף פיזי אחרי שהוא יוצא מ-Working Set.
למה תיקייה משותפת ב-Windows עובדת לפעמים ונכשלת בפעמים אחרות — troubleshooting של Kerberos, NTLM ו-Credentials
מאבחנים גישה לסירוגין לתיקייה משותפת ב-Windows לפי תסמינים ולוגים. בודקים שמות מול כתובות IP, כשלים רק באפליקציה, סיסמאות ריקות, שגיאה 12...
DllMain ו-loader lock — הסיבה האמיתית שאומרים לא לעשות כלום ב-initialization של DLL
למה אסור לקרוא ל-LoadLibrary או להסתנכרן עם threads מתוך DllMain. המאמר מסביר ממקורות ראשוניים איך loader lock מסדר כל DLL notification, ...
מעמקי ה-I/O ב-Windows (פרק 6, אחרון) — filter drivers ו-minifilter: למה Procmon וסריקת אנטי-וירוס יכולים להתערב ב-I/O
הפרק האחרון בסדרה שמסבירה בתרשימים filter drivers ו-minifilter ב-Windows. המאמר עובר על Filter Manager ו-altitude, callbacks מסוג pre/pos...
מעמקי ה-I/O ב-Windows (פרק 5) — המבנה הפנימי של NTFS: מערכת קבצים מתוך MFT
פרק 5 בסדרה שמסבירה בתרשימים את המבנה הפנימי של NTFS. המאמר עובר על MFT ו-file records, כמה data streams (Zone.Identifier), hard links וש...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
פיתוח יישומי Windows
יישומים עסקיים, חיבור התקנים וכלי תקשורת, מהגדרת הדרישות ועד הפיתוח.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- האם עמודת Memory ב-Task Manager מציגה את כל הזיכרון שהאפליקציה הקצתה?
- לא. ל-Task Manager יש כמה עמודות זיכרון — משפחת Working Set, משפחת Private Working Set, Commit Size ואחרות — והמשמעות תלויה באיזה מסך ובאיזו עמודה מסתכלים. Working Set הוא ה-pages שכרגע resident ב-RAM; Private Bytes או Commit Size הוא כמות ה-Commit הפרטית של אותו process. אל תקראו עמודה אחת בשם Memory כקיבולת הכוללת שהאפליקציה הקצתה, או כגודל של memory leak.
- מה ההבדל בין Working Set ל-Private Bytes?
- Working Set הוא כמות ה-pages שנראים ל-process ושכרגע resident ב-RAM הפיזי, כולל pages שאפשר לשתף כמו קוד DLL ו-memory-mapped files. Private Bytes הוא כמות הזיכרון ה-committed שמשמשת באופן בלעדי את אותו process, בין אם הוא כרגע resident ב-RAM ובין אם לא. לכן השניים אף פעם לא שווים, ואף אחד מהם לא גדול יותר מהשני באופן קבוע.
- האם Committed 18/32GB ב-Task Manager אומר שנכתבו 18GB ל-pagefile?
- לא. הנתון משמאל הוא סך ה-Commit Charge שהמערכת כולה סופרת כרגע; מימין זו תקרת ה-Commit שהמערכת יכולה לתמוך בה. התקרה נקבעת בערך לפי RAM ועוד pagefile, אבל לא כל הסכום משמאל יושב בפועל ב-pagefile. רוב ה-pages ה-committed נמצאים ב-RAM, ולחלק מה-pages ה-committed מעולם לא הוקצה page פיזי. במקביל, pages שאפשר לטעון מחדש מקובץ המקור — כמו EXE, DLL ו-memory-mapped files — לא בהכרח מעלים את ה-Commit הפרטי באותה כמות שבה הם מעלים את ה-Working Set.
- האם אפשר לקבל OutOfMemory גם כשיש RAM פנוי?
- כן. allocation יכול להיכשל מסיבות שאינן RAM פיזי, כולל process של 32-bit שמכלה את ה-virtual address space, מחסור בטווח כתובות פנוי ורציף, תקרת ה-Commit של המערכת, או מגבלות ייחודיות ל-Job Object או ל-runtime. בפרט, process של 32-bit על Windows של 64-bit מוגבל בדרך כלל ל-2GB של user-mode virtual address space, אלא אם הוא Large Address Aware.
- האם השבתת ה-pagefile הופכת את Windows למהיר יותר?
- אי אפשר להניח את זה ככלל. השבתת pagefile מורידה את תקרת ה-Commit של המערכת, מקשה לפנות מ-RAM pages שסומנו Modified ואינם בשימוש, ומשפיעה על הגדרת ה-crash dump. את גודל ה-pagefile קובעים לפי מדידת שיא ה-Commit Charge ולפי ה-crash dump שצריך — זו לא הגדרה שכובים בלי הצדקה.
- האם Page Faults/sec גבוה אומר שהמערכת קצרה בזיכרון?
- אי אפשר לדעת מזה לבד. Page Fault כולל soft fault, שאפשר לפתור מ-Standby pages ב-RAM או מ-pages משותפים עם process אחר, ו-hard fault שקורא מהדיסק. במקום להסתכל על Page Faults/sec בבידוד, בדקו Pages Input/sec, Page Reads/sec, Available MBytes, disk latency וזמן עיבוד יחד על אותו ציר זמן.