איסוף אשפה ב-Go
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 23 מתוך 107.
בניגוד לשפות כמו C או C++, Go מנהלת את הזיכרון באופן אוטומטי באמצעות איסוף אשפה (GC). אין צורך לשחרר את הזיכרון באופן ידני כשמסיימים להשתמש בו. סביבת הריצה של Go עוקבת אחר הזיכרון שעדיין נמצא בשימוש ומשחררת את השאר.
כשמקצים זיכרון באמצעות new(), make() או על ידי יצירת משתנים, אוסף האשפה של Go עוקב אחר ההקצאות האלה. ברגע שערך כבר אינו נגיש מאף חלק בתוכנית שלך, ה-GC משחרר את הזיכרון הזה באופן אוטומטי.
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // p מועבר לערימה, GC ינהל אותו
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// כאשר כבר לא משתמשים ב-person, GC משחרר את הזיכרון
}בדוגמה הזו, המבנה Person מוקצה בערימה משום שהוא מוחזר מהפונקציה. המהדר של Go מבצע ניתוח בריחה כדי לקבוע אם משתנה יכול להישאר במחסנית או שחייב לעבור לערימה. אוסף הזבל מנהל רק זיכרון שבערימה.
ניהול הזיכרון האוטומטי הזה מאפשר לך להתמקד בבניית האפליקציה שלך בלי לדאוג לדליפות זיכרון שנגרמות משחרור זיכרון שנשכח. עם זאת, ההבנה שקיים GC עוזרת לך לכתוב קוד יעיל יותר על ידי צמצום הקצאות מיותרות כשחשובה הביצועים.
אתגר
קלבואו נבנה מנהל הפעלות שמדגים כיצד אוסף האשפה של Go מטפל בזיכרון של אובייקטים שבורחים לערימה. תיצרו הפעלות שמוקצות באופן דינמי ומוחזרות מפונקציות, וכך תאפשרו ל-GC לנהל את מחזור החיים שלהן.
תארגנו את הקוד שלכם בשני קבצים:
session.go: הגדירו מבנהSessionעם השדותID(מחרוזת),Username(מחרוזת) ו-Data(מערך של מחרוזות). צרו פונקציהNewSessionשמקבלת מזהה ושם משתמש, מקצה Session חדש בערימה (על ידי החזרת מצביע) ומאתחלת את מערך ה-Data כריק. הוסיפו גם מתודהAddDataעם מקבל מסוג מצביע, שמוסיפה מחרוזת למערך ה-Data של ההפעלה, ומתודהSummaryשמחזירה מחרוזת מעוצבת המציגה את פרטי ההפעלה.main.go: קראו את פרטי ההפעלה מהקלט, צרו הפעלה באמצעות פונקציית הבנאי שלכם, הוסיפו לה כמה רשומות נתונים והציגו את סיכום ההפעלה. מכיוון שההפעלה נוצרת בתוך פונקציה ומוחזרת כמצביע, היא בורחת לערימה, שם אוסף האשפה ינהל אותה.
הקלטים הבאים יסופקו:
- שורה 1: מזהה ההפעלה
- שורה 2: שם המשתמש
- שורה 3: רשומת הנתונים הראשונה להוספה
- שורה 4: רשומת הנתונים השנייה להוספה
המתודה Summary שלכם צריכה להחזיר מחרוזת בפורמט הבא:
Session [ID] for user [Username]
Data entries: [count]
- [entry1]
- [entry2]לדוגמה, עבור sess-001, alice, login ו-view_dashboard, הפלט שלכם צריך להיות:
Session sess-001 for user alice
Data entries: 2
- login
- view_dashboardהתובנה המרכזית כאן היא שהפונקציה NewSession שלכם יוצרת Session ומחזירה מצביע אליו. כתוצאה מכך, ה-Session "בורח" לערימה במקום להישאר במחסנית, ולכן הוא כשיר לאיסוף אשפה ברגע שלא נותרות אליו הפניות. הקוד שלכם לא צריך לשחרר את הזיכרון הזה באופן ידני: Go מטפלת בכך אוטומטית.
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// קרא את מזהה ההפעלה
sessionID, _ := reader.ReadString('\n')
sessionID = sessionID[:len(sessionID)-1]
// קרא את שם המשתמש
username, _ := reader.ReadString('\n')
username = username[:len(username)-1]
// קרא את רשומת הנתונים הראשונה
data1, _ := reader.ReadString('\n')
data1 = data1[:len(data1)-1]
// קרא את רשומת הנתונים השנייה
data2, _ := reader.ReadString('\n')
data2 = data2[:len(data2)-1]
// TODO: צור הפעלה חדשה באמצעות הפונקציה NewSession
// ההפעלה תועבר לערימה מכיוון שאנו מחזירים מצביע
// TODO: הוסף את רשומות הנתונים להפעלה באמצעות המתודה AddData
// TODO: הדפס את סיכום ההפעלה באמצעות המתודה Summary
fmt.Println("")
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין