בעבר, מערכת Android תמכה רק בדפים עם זיכרון בגודל 4KB, מה שאפשר למערכת לייעל את הביצועים של הזיכרון הכולל במכשירים עם נפח זיכרון ממוצע. החל מ-Android 15, AOSP תומך במכשירים שמגדירים בהם גודל דף של 16KB (מכשירים עם 16KB). אם האפליקציה שלך משתמשת בספריות NDK, באופן ישיר או בעקיפין דרך SDK, תצטרכו לבנות מחדש את האפליקציה כדי שהיא תפעל במכשירים האלה עם 16 KB.
יצרני המכשירים ממשיכים לפתח מכשירים עם נפחים גדולים יותר של זיכרון פיזי (RAM). במכשירים רבים כאלה יוטמעו דפים בגודל 16KB (ובסופו של דבר בגודל גדול יותר) כדי לשפר את הביצועים של המכשיר. הוספת תמיכה במכשירים עם גודל דף של 16KB מאפשרת לאפליקציה לפעול במכשירים האלה, וליהנות משיפורי הביצועים שקשורים לכך. בלי קומפילציה מחדש, האפליקציות לא יפעלו במכשירים עם 16 KB בגרסאות עתידיות של Android.
כדי לעזור לכם להוסיף תמיכה לאפליקציה, סיפקנו הנחיות בנושא בדיקה אם יש השפעה על האפליקציה, בנייה מחדש של האפליקציה (אם רלוונטי) ובדיקת האפליקציה בסביבה של 16KB באמצעות אמולטורים (כולל תמונות מערכת של Android 15 לאמולטור של Android).
דרישת התאימות של Google Play
כדי לוודא שהאפליקציה פועלת בצורה תקינה בגרסאות העדכניות ביותר של Android, כל האפליקציות ב-Google Play שמטרגטות ל-Android 15 (רמת API 35) ומעלה חייבות לתמוך בדפים עם זיכרון בגודל 16KB במכשירי 64 ביט. החל מ-1 בפברואר 2027, אם העדכונים לאפליקציה לא יתמכו בדפים עם זיכרון בגודל 16KB, לא תהיה לך אפשרות לפרסם אותם.
היתרונות והשיפורים בביצועים
במכשירים שמוגדרים בהם דפים בגודל 16KB, נעשה שימוש במעט יותר זיכרון בממוצע, אבל יש גם שיפורים שונים בביצועים של המערכת ושל האפליקציות:
- זמני השקה קצרים יותר של אפליקציות בזמן שהמערכת נמצאת בלחץ על הזיכרון: נמוך ב-3.16% בממוצע, עם שיפורים משמעותיים יותר (עד 30%) בחלק מהאפליקציות שבדקנו
- צריכת חשמל מופחתת במהלך השקת האפליקציה: הפחתה של 4.56% בממוצע
- הפעלה מהירה יותר של המצלמה: הפעלה מתוך הזיכרון (hot start) מהירה יותר ב-4.48% בממוצע, והפעלה מחדש (cold start) מהירה יותר ב-6.60% בממוצע
- זמן הפעלה משופר של המערכת: שיפור של 8% (כ-950 אלפיות השנייה) בממוצע
השיפורים האלה מבוססים על הבדיקה הראשונית שלנו, וסביר להניח שהתוצאות במכשירים בפועל יהיו שונות. נמשיך לספק ניתוח נוסף של היתרונות הפוטנציאליים של האפליקציות.
איך בודקים אם האפליקציה מושפעת מהשינוי
אם האפליקציה שלך משתמשת בקוד Native, עליך לבנות מחדש את האפליקציה עם תמיכה במכשירים עם דפי זיכרון בגודל 16 KB. אם אתם לא בטוחים אם האפליקציה שלכם משתמשת בקוד Native, אתם יכולים להשתמש בכלי APK Analyzer כדי לזהות אם יש קוד Native, ואז לבדוק את ההתאמה של פלחי ELF לכל הספריות המשותפות שמצאתם. ב-Android Studio יש גם תכונות שעוזרות לכם לזהות באופן אוטומטי בעיות שקשורות ליישור.
אם האפליקציה שלך משתמשת רק בקוד שנכתב בשפת התכנות Java או ב-Kotlin, כולל כל הספריות או ערכות ה-SDK, אז האפליקציה כבר תומכת במכשירים עם נפח אחסון של 16 KB. עם זאת, מומלץ לבצע בדיקה של האפליקציה בסביבה של 16 KB כדי לוודא שאין נסיגות לא צפויות בהתנהגות האפליקציה.
האם באפליקציה שלך נעשה שימוש בקוד Native?
האפליקציה שלכם משתמשת בקוד Native אם מתקיים אחד מהתנאים הבאים:
- האפליקציה משתמשת בקוד C/C++ (קוד Native). אם האפליקציה משתמשת ב-Android NDK, היא משתמשת בקוד Native.
- האפליקציה מקושרת לספריות Native או לתלויות של צד שלישי (כמו ערכות SDK) שמשתמשות בהן.
- האפליקציה שלך פותחה באמצעות כלי של צד שלישי לפיתוח אפליקציות, שמשתמש בספריות Native במכשיר.
זיהוי ספריות Native באמצעות הכלי לניתוח APK
הכלי לניתוח APK מאפשר להעריך היבטים שונים של קובץ APK שנוצר. כדי לבדוק אם האפליקציה משתמשת בקוד Native (גם אם היא תואמת לדפים בגודל 16KB):
- פותחים את Android Studio, לוחצים על File > Open (קובץ > פתיחה) ובוחרים פרויקט כלשהו.
בסרגל התפריטים, לוחצים על Build > Analyze APK... (פיתוח > ניתוח APK...).
בוחרים את ה-APK שרוצים לנתח.
מחפשים בתיקייה
lib, שמכילה קובצי אובייקטים משותפים (.so) אם יש כאלה. אם יש קובצי אובייקטים משותפים, האפליקציה משתמשת בקוד Native. בעמודה Alignment מוצגות אזהרות לגבי קבצים שיש בהם בעיות תאימות. אם אין קבצי אובייקט משותפים או אם אין תיקייה בשםlib, סימן שהאפליקציה לא משתמשת בקוד Native.
זיהוי בעיות ביישור באמצעות בדיקות אוטומטיות
Android Studio מציג אזהרה באופן יזום אם הספריות או קובצי ה-APK המובנים מראש לא עומדים בדרישות של 16 KB. משתמשים בכלי לניתוח APK כדי לבדוק אילו ספריות צריך לעדכן או אם נדרשים שינויים בקוד.
Lint ב-Android Studio גם מדגיש ספריות Native שלא מותאמות לדפים בגודל 16KB.
בדיקת ההתאמה של מקטעי ELF לספריות משותפות
בספריות משותפות, צריך לוודא שפלח ה-ELF של הספריות המשותפות מיושר בצורה תקינה באמצעות יישור ELF בגודל 16 KB. אם אתם מפתחים ב-Linux או ב-macOS, אתם יכולים להשתמש בסקריפט check_elf_alignment.sh כמו שמתואר בקטע הבא. אפשר גם להשתמש ישירות בכלי שורת הפקודה.
שימוש בסקריפט check_elf_alignment.sh (ב-Linux או ב-macOS)
כדי לבדוק את היישור של מקטעי ELF באמצעות הסקריפט check_elf_alignment.sh, פועלים לפי השלבים הבאים:
שומרים את הסקריפט
check_elf_alignment.shבקובץ.מריצים את הסקריפט בקובץ ה-APK של האפליקציה:
check_elf_alignment.sh APK_NAME.apkהסקריפט מפיק את הפלט
ALIGNEDאוUNALIGNEDלכל הספריות המשותפות שלarm64-v8a.אם יש ספריות משותפות מסוג
arm64-v8aאוx86_64שהןUNALIGNED, צריך לעדכן את האריזה של הספריות האלה, ואז לקמפל מחדש את האפליקציה ולבדוק אותה מחדש לפי השלבים שמתוארים בקטע הזה.
שימוש ישיר בכלי שורת הפקודה
כדי לבדוק את ההתאמה של מקטעי ELF באמצעות כלי שורת הפקודה, פועלים לפי השלבים הבאים:
- מוודאים שגרסה 35.0.0 ואילך של Android SDK Build-Tools ו-Android NDK מותקנים באמצעות SDK Manager ב-Android Studio או באמצעות כלי שורת הפקודה
sdkmanager. מחילוץ קובץ ה-APK של האפליקציה:
Linux או macOS
unzip APK_NAME.apk -d /tmp/my_apk_outWindows (PowerShell)
Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_outבספרייה הזמנית שחילצתם אליה את קובץ ה-APK, בודקים את התוכן של הספרייה
libכדי למצוא קובצי אובייקטים משותפים (.so). אלה אותם קובצי אובייקטים משותפים שהייתם רואים בזמן זיהוי ספריות Native באמצעות הכלי לניתוח APK. מריצים את הפקודה הבאה על כל קובץ של אובייקט משותף:Linux או macOS
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p SHARED_OBJECT_FILE.so | grep LOADWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-objdump.exe -p SHARED_OBJECT_FILE.so | Select-String -Pattern "LOAD"כאשר
SDK_ROOT_LOCATIONהוא הנתיב לספרייה שבה התקנתם את Android SDK,SHARED_OBJECT_FILEהוא השם של קובץ האובייקט המשותף שאתם בודקים ו-NDK_VERSIONהוא הגרסה של Android NDK שהתקנתם (לדוגמה,28.0.12433566). הפלט ייראה בערך כמו בדוגמה הבאה לכל קובץ שאתם בודקים:LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**14 LOAD off 0x0000000000042a90 vaddr 0x0000000000043a90 paddr 0x0000000000043a90 align 2**14 LOAD off 0x0000000000046230 vaddr 0x0000000000048230 paddr 0x0000000000048230 align 2**14בודקים את שורות הפלט כדי לוודא שאין ערכים קטנים מ-
2**14בקטעי הטעינה. אם יש קטעי טעינה עם הערכים2**13,2**12או ערכים נמוכים יותר, תצטרכו לעדכן את האריזה של הספריות האלה, לקמפל מחדש את האפליקציה ולבדוק אותה שוב לפי השלבים בקטע הזה.לאחר מכן, מריצים את כלי שורת הפקודה
zipalignבקובץ ה-APK של האפליקציה:Linux או macOS
SDK_ROOT_LOCATION/Android/sdk/build-tools/35.0.0/zipalign -v -c -P 16 4 APK_NAME.apkWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apkSDK_ROOT_LOCATIONהוא הנתיב לספרייה שבה התקנתם את Android SDK, ו-APK_NAMEהוא השם של קובץ ה-APK של האפליקציה. אם כל הספריות המשותפות מיושרות בצורה נכונה, בשורה האחרונה של הפלט יופיע הכיתוב 'האימות הצליח'.אם האימות נכשל, צריך לשנות את היישור של חלק מהספריות המשותפות. לשם כך, צריך לעדכן את האריזה של הספריות האלה, לקמפל מחדש את האפליקציה ולבדוק אותה שוב לפי השלבים שמפורטים בקטע הזה.
בדיקת דגל האבטחה של RELRO
כדי לצמצם את הסיכון לניצול פרצות אבטחה, מקשרים מודרניים משתמשים בדגל Relocation Read-Only (RELRO) כדי להפוך את קטעי ההעברה של קובץ האובייקט המשותף לקריאה בלבד אחרי הטעינה. מפעילים את הדגל RELRO ב-build.
אם קטע RELRO כולל כתובת התחלה וגודל פלח (MemSize) שלא מותאמים ל-16KB, האפליקציה קורסת בזמן הריצה עם שגיאת פילוח. זה קורה אם הקובץ .so נוצר באמצעות ערכת הכלים NDK r27 ומטה בלי להפעיל את הדגלים הרלוונטיים.
מריצים את הפקודה הבאה בכל קובץ אובייקט משותף (Linux או macOS):
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf -Wl SHARED_OBJECT_FILE.so | grep 'RELRO\|Type'
המחרוזת GNU_RELRO מודפסת אם יש קטע RELRO.
בשלב הבא, בודקים את ההתאמה של קטע ה-RELRO על ידי חיבור כתובת ההיסט הווירטואלית שלו (VirtAddr) עם גודל הזיכרון של הקטע (MemSiz) וחלוקה ב-16KB (0x4000). אם השארית (מודולו) היא אפס, קטע ה-RELRO תואם ל-16KB.
דוגמה לקובץ .so לא מותאם:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R 0x1
נוסחה: (VirtAddr + MemSiz) % 0x4000 == 0
התוצאה: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000
הערך 0x1000 לא שווה לאפס, ולכן קובץ ה-.so הזה לא עומד בדרישות של 16 KB. טווח ההגנה של RELRO מ-DC000 (מעבר הדף הקודם) עד E4000 הוא לקריאה בלבד, אבל Android Linker מצפה שטווח המשנה מ-E1000 עד E4000 יהיה ניתן לכתיבה, ולכן מתרחשת שגיאת פילוח. במקרה כזה, צריך לבנות מחדש את הקובץ .so כמו שמתואר בקטע הידור האפליקציה באמצעות יישור ELF בגודל 16KB.
דוגמה לקובץ .so מותאם עם קטע RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x00510 R 0x1
דוגמה לקובץ .so בלי קטע RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
זהו קובץ .so שתואם באופן טריוויאלי לדפים בגודל 16KB, בלי קטע RELRO.
דוגמה לקובץ .so מיושר שבו GNU_RELRO נמצא בקטע LOAD משלו:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 RW 0x4000
GNU_RELRO 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 R 0x1
נוסחה: (VirtAddr + MemSiz) % 0x4000 == 0
תוצאה: (0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000
למרות ש-0x1000 הוא לא אפס, קובץ ה-.so הזה עדיין תואם ל-16KB.
נוסחת המודולו חלה כש-GNU_RELRO חולק פלח LOAD עם נתונים שניתנים לכתיבה (כמו .data או .bss).
כדי לבדוק אם GNU_RELRO נמצא בפלח LOAD משלו, בודקים את כותרות התוכנית על ידי הרצת הפקודה llvm-readelf -Wl וסינון באמצעות grep 'LOAD\|RELRO\|Type'. השוו בין VirtAddr ו-MemSiz של GNU_RELRO לבין הפלח LOAD שמכיל אותם. בדוגמה הזו, לשני המשתמשים יש את ההרשאות VirtAddr 0x4680 ו-MemSiz 0x0980.
מכיוון שהפלח LOAD כולו מכיל רק נתוני RELRO, אין נתונים שאפשר לכתוב בטווח המשנה מ-0x5000 עד 0x8000. בנוסף, כל קטע LOAD שאפשר לכתוב בו מתחיל בגבול של דף נפרד בגודל 16KB (ב-0x8000 ומעלה). ההגנה של הכלי לקישור תגיות לקריאה בלבד לא תהיה חופפת לנתונים שניתנים לכתיבה, ולכן לא תתרחש שגיאת פילוח.
איך מפתחים אפליקציה שתומכת במכשירים עם דפים בגודל 16KB
אם האפליקציה שלכם משתמשת בקוד Native, בצעו את השלבים שמפורטים בסעיפים הבאים כדי לוודא שהאפליקציה תומכת במכשירים עם דפי זיכרון בגודל 16KB:
- עדכון האריזה של הספריות המשותפות
- קומפילציה של האפליקציה באמצעות יישור ELF בגודל 16 KB
- תיקון קוד ופתרון בעיות בזמן ריצה
- אופטימיזציה של מקציבי זיכרון מותאמים אישית (אם רלוונטי)
- בדיקה אם ערכות ה-SDK תומכות ב-16KB
עדכון האריזה של הספריות המשותפות
כדאי לשדרג לגרסה 8.5.1 ומעלה של AGP ולהשתמש בספריות משותפות לא דחוסות.
משתמשים ב-bundletool כדי לוודא שהרוכסן ישר
כדי לראות את היישור של החבילה, משתמשים באפשרות:
bundletool dump config --bundle=<my .aab> | grep alignment
אם מופיע PAGE_ALIGNMENT_16K, סימן שה-bundle מבקש יישור ZIP של 16KB. אם מופיע PAGE_ALIGNMENT_4K, זה אומר שקובץ ה-APK שנוצר מקובץ ה-AAB הזה צריך להכיל קובצי .so מיושרים בגודל 4KB בקובץ ה-ZIP.
AGP בגרסה 8.5.1 ואילך
במכשירים עם דפי זיכרון בגודל 16KB, צריך להגדיר את האפליקציות שכוללות ספריות משותפות לא דחוסות כך שהן יתאימו לגבולות של קובצי ZIP בגודל 16KB. כדי לעשות את זה, צריך לשדרג לגרסה 8.5.1 ואילך של פלאגין של Android Gradle (AGP). פרטים על תהליך השדרוג זמינים בקטע כלי העזרה לשדרוג הפלאגין של Android Gradle.
AGP בגרסה 8.5 ומטה
אם אתם לא יכולים לשדרג את AGP לגרסה 8.5.1 ואילך, אתם יכולים לעבור להשתמש בספריות משותפות דחוסות. כדי למנוע בעיות בהתקנת האפליקציה שקשורות לספריות משותפות לא מיושרות, צריך לעדכן את הגדרת Gradle כך ש-Gradle ידחוס את הספריות המשותפות בזמן אריזת האפליקציה.
מגניב
מוסיפים את האפשרות הבאה לקובץ build.gradle:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
Kotlin
בקובץ build.gradle.kts, מוסיפים את האפשרות הבאה:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
AGP מגרסה 8.0 ומטה
אם אתם משתמשים בגרסה 8.0 או בגרסה ישנה יותר של AGP, אתם צריכים גם להשבית את האפשרות של ספריית Native לא דחוסה לחבילות אפליקציות בקובץ gradle.properties:
android.bundle.enableUncompressedNativeLibs=false
קומפלו את האפליקציה באמצעות יישור ELF בגודל 16KB
כדי שהאפליקציה תפעל במכשירים עם 16 KB, צריך ליישר את פלחי ה-ELF של הספריות המשותפות באמצעות יישור ELF של 16 KB.
מפתחי משחקים שהמשחק שלהם פועל על מנוע המשחק Unity יכולים לעיין במדריך של Unity. אם המשחק פועל על גבי מנוע המשחק Unreal, עיינו במדריך של Unreal.אם מדובר במנועי משחק מקוריים, המשיכו לקרוא את המדריך הזה.
כדי להדר את האפליקציה באמצעות יישור ELF בגודל 16KB, צריך לבצע את השלבים באחד מהקטעים הבאים, בהתאם לגרסת Android NDK שבה אתם משתמשים.
Android NDK r28 ואילך
גרסת NDK r28 ואילך מקמפלת כברירת מחדל עם יישור של 16KB.
Android NDK r27 וגרסאות קודמות
כדי לתמוך בקומפילציה של ספריות משותפות עם יישור של 16 KB באמצעות Android NDK בגרסה r27 ומטה, צריך להשתמש בדגלי ה-linker הבאים:
-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384
כך מעדכנים את קובצי התצורה של מערכת ה-build:
ndk-build
אם אתם משתמשים ב-ndk-build, עדכנו את Android.mk כדי להפעיל יישור ELF של 16KB:
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
CMake
אם אתם משתמשים ב-CMake, צריך לעדכן את CMakeLists.txt כדי להפעיל יישור ELF של 16 KB:
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,-z,max-page-size=16384"
"-Wl,-z,common-page-size=16384"
)
תיקון קוד ופתרון בעיות בזמן ריצה
גם אם האפליקציה שלך מותאמת ל-16KB, יכול להיות שיופיעו בה שגיאות אם יש מקומות בקוד שבהם מניחים שמכשיר מסוים משתמש בגודל דף ספציפי. כדי למנוע את זה, צריך לבצע את הפעולות הבאות:
מסירים מהלוגיקה של הקוד תלות שמוגדרת בהארדקוד ומפנה לקבוע
PAGE_SIZEאו למופעים, שבהם מניחים שגודל הדף של המכשיר הוא 4 KB (4096).במקום זאת, צריך להשתמש ב-
getpagesize()או ב-sysconf(_SC_PAGESIZE).חפשו שימושים ב-
mmap()ובממשקי API אחרים שדורשים ארגומנטים מותאמים לדף, והחליפו אותם בחלופות לפי הצורך.
במקרים מסוימים, אם האפליקציה משתמשת ב-PAGE_SIZE כערך נוח שלא קשור לגודל הדף הבסיסי, השימוש בערך הזה במצב 16KB לא יגרום לאפליקציה להפסיק לפעול. עם זאת, אם הערך הזה מועבר לליבה באמצעות mmap בלי MAP_FIXED, הליבה עדיין משתמשת בדף שלם, מה שגורם לבזבוז של חלק מהזיכרון. לכן, PAGE_SIZE לא מוגדר כשמצב 16KB מופעל ב-NDK r27 ואילך.
אם האפליקציה משתמשת ב-PAGE_SIZE באופן הזה, ואף פעם לא מעבירה את הערך הזה ישירות לליבה, במקום להשתמש ב-PAGE_SIZE, צריך ליצור משתנה חדש עם שם חדש כדי לציין שהוא משמש למטרות אחרות ולא מייצג דף זיכרון אמיתי.
אופטימיזציה של מקצי זיכרון מותאמים אישית
במערכות של 16KB, יחידת הזיכרון הפיזי הקטנה ביותר שמערכת ההפעלה מקצה גדולה פי ארבעה מאשר במערכות של 4KB. אם מקצה מותאם אישית תוכנן על סמך הנחות של 4KB, הוא יכול לפזר אובייקטים קטנים על פני כמה דפים של 16KB ולשמור זיכרון ריק שלא לצורך. הפעולה הזו יכולה להגדיל באופן משמעותי את השימוש בזיכרון הפיזי (RSS) ולפגוע ביעילות של החלפה דחוסה (ZRAM).
אם הקוד שלכם מנהל מאגרי זיכרון משלו, כדאי לפעול לפי ההמלצות הבאות:
1. הימנעות מספי בייט שמוגדרים בתוך הקוד בשביל לפנות זיכרון
הרבה מקצים משתמשים במגבלות בייטים קבועות כדי להחליט מתי לשחרר זיכרון בחזרה למערכת ההפעלה באמצעות madvise(MADV_DONTNEED) (לדוגמה, לשחרר זיכרון רק אם אובייקטים פעילים תופסים פחות מ-8KB).
במערכת של 16 KB, אפילו אובייקט פעיל יחיד של 16 בייט מצמיד דף שלם של 16 KB (שהוא גדול מ-8 KB). כתוצאה מכך, אף פעם לא מושג סף השחרור, והקצאת הזיכרון אף פעם לא מחזירה לליבה את הזיכרון הסמוך שלא נמצא בשימוש.
מה עושים: לעולם לא משתמשים בקבועי בייטים שמוגדרים בתוך הקוד לשיטות היוריסטיות של שחרור דפים.
שינוי דינמי של ספי השחרור בזמן הריצה בהתאם לגודל הדף בפועל באמצעות sysconf(_SC_PAGESIZE).
2. מילוי דפים שכבר נעשה בהם שימוש (הקצאה צפופה תחילה)
אם מקצה הזיכרון מחלק את הזיכרון לפי סדר כניסה (FIFO) או לפי שיטת רובין, הקצאות חדשות מתפזרות על פני הרבה דפים בגודל 16KB שמולאו באופן חלקי. אובייקט יחיד בדף שומר את כל ה-16 KB בזיכרון ה-RAM הפיזי.
מה לעשות: תמיד להקצות אובייקטים חדשים מהדף או מה-slab המלאים ביותר (הצפופים ביותר) לפני שנוגעים בדפים ריקים או בדפים שנעשה בהם שימוש מועט. הקצאה של דפים חדשים לדפים שכבר נמצאים בשימוש מאפשרת לדפים שנעשה בהם שימוש מועט להתרוקן באופן טבעי עד שלא נשארים בהם אובייקטים פעילים, כך שאפשר לשחרר את כל הדף בגודל 16KB למערכת ההפעלה.
3. התאמת מאגרי הזיכרון ל-16 KB ושמירה על גודל מאגר בינוני
במערכות של 4KB, טווחים של כמה דפים שנועדו לשימוש במערכות של 4KB יכולים להכיל עודף של זיכרון לא פנוי בלי שחרור. בנוסף, מחלקות גודל שלא מתחלקות באופן שווה ל-16KB גורמות לפרגמנטציה בסוף כל דף.
מה לעשות:
- חשוב לוודא שכל מאגרי הזיכרון, גבולות ה-slab והיישורים של המאגרים הם כפולות מדויקות של גודל הדף בזמן הריצה.
- צריך להעריך מחדש את גודל טווח הדפים של מחלקות אובייקטים קטנים כדי להימנע מהקצאה של חלקי זיכרון גדולים מדי שגורמים לזיכרון לא פעיל להיתקע.
4. שחרור זיכרון פיזי למאגרים גדולים במטמון באופן מיידי
בדרך כלל, מקצים מותאמים אישית שומרים במטמון מאגרי נתונים גדולים (> 64 KB) במאגר בזיכרון, כדי שאפשר יהיה לעשות בהם שימוש חוזר בלי לשלם את התקורה של קריאות המערכת mmap או munmap. עם זאת, שמירת המאגרים המלוכלכים האלה בזיכרון מבזבזת מגה-בייט של זיכרון RAM פיזי בזמן ההמתנה לטיימר של פינוי.
מה עושים: שומרים את טווח כתובות הזיכרון הווירטואלי לשימוש חוזר מהיר, אבל קוראים ל-madvise(..., MADV_DONTNEED) או ל-madvise(..., MADV_FREE) באופן מיידי כשמחזירים מאגר לזיכרון המטמון. מערכת ההפעלה מחזירה את ה-RAM הפיזי מיד, והאפליקציה עדיין יכולה להשתמש שוב בכתובת הווירטואלית באופן מיידי בלי להקצות אותה מחדש.
5. איפוס הזיכרון בזמן שחרור במקום בזמן הקצאה לדחיסת ZRAM
מערכת Android משתמשת בהחלפה דחוסה (ZRAM) כדי לשמור אפליקציות ברקע בזיכרון. במכשירים עם דפים בגודל 16KB, אם דף מכיל אפילו אובייקט פעיל אחד, כל הדף בגודל 16KB נשאר בזיכרון או מועבר ל-ZRAM. נתוני זבל שנשארו (כמו מצביעים ומחרוזות לא עדכניים) בחלקים בדף שפעם היו פנויים, לא נדחסים טוב.
אם הקצאת הזיכרון מאפסת את הזיכרון (למשל לצורכי אבטחה או הקצאות עם אתחול לאפס), כדאי לאפס את הזיכרון כשמבטלים את ההקצאה (free()) ולא כשמבצעים את ההקצאה:
- דפים מוצמדים עולים פחות: זיכרון לא פעיל בדפים בגודל 16KB שמלאים באופן חלקי נדחס כמעט לאפס ב-ZRAM, כך שדפים מוצמדים לא מבזבזים שטח החלפה פיזי.
- תקורה מינימלית של מטמון: כשמתבצעת קריאה ל-
free(), הזיכרון כבר חם במטמון של המעבד (CPU), וכך נמנעות <b>אי מציאות במטמון</b> נוספות בהמשך.
סיכום ההמלצות
| אזור | המלצה | ההשפעה הצפויה |
|---|---|---|
| ספי טיהור | התאמה דינמית של ערכי הסף לפרסום באמצעות sysconf(_SC_PAGESIZE). |
מונע מצב של קיפאון לוגי קבוע בגלל שחרור. |
| הקצאת הזמנה | הקצאה מתבצעת קודם מהדף או מהבלוק הכי צפוף (כמעט מלא). | מפחית את הזיכרון התושב (RSS) על ידי אריזת אובייקטים פעילים יחד. |
| Span sizing | צריך להתאים את מחלקות הגודל לכפולות של 16KB ולהימנע מגושים גדולים מדי. | מונע פרגמנטציה של דפי זנב ומפחית את הזיכרון המבוזבז. |
| מטמון של מאגרים זמניים | צריך להפעיל את madvise(MADV_DONTNEED) מיד כששומרים מאגרים גדולים במטמון. |
התכונה הזו מונעת ניפוח של זיכרון RAM לא פעיל, תוך שמירה על שימוש חוזר וירטואלי מהיר. |
| Swap / ZRAM | איפוס הזיכרון ב-free() במקום בהקצאה. |
משפר את יחסי הדחיסה של ZRAM, כך שדפים מוצמדים צורכים פחות משאבים. |
בדיקה אם ערכות ה-SDK תומכות ב-16KB
הרבה ערכות SDK תואמות לגודל דף של 16 KB, במיוחד אם אתם בונים אותן בעצמכם או משתמשים בגרסאות עדכניות של ערכות SDK מוכנות מראש. עם זאת, חלק מהגרסאות של ה-SDK או מהגרסאות שנוצרו מראש לא תואמות ל-16KB, לכן צריך לבדוק באתר של כל ספק SDK באיזו גרסה להשתמש עם 16KB.
בדיקת האפליקציה בסביבה של 16 KB
אחרי שיוצרים את האפליקציה עם תמיכה במכשירים עם דפים בגודל 16KB, כדאי לבדוק את האפליקציה בסביבה של 16KB כדי לראות אם חוויית השימוש באפליקציה חווה רגרסיות. לשם כך, בצע את הצעדים הבאים:
מגדירים את Android 15 SDK ואילך.
מגדירים את אחת מסביבות הבדיקה הבאות:
מפעילים את מכשיר הבדיקה ומריצים את הפקודה הבאה כדי לוודא שהוא משתמש בסביבה של 16KB:
adb shell getconf PAGE_SIZEהפקודה אמורה להחזיר את הערך
16384.מריצים את הפקודה הבאה
zipalignכדי לוודא שהאפליקציה מיושרת ל-16 KB, כאשר APK_NAME הוא שם קובץ ה-APK של האפליקציה:zipalign -c -P 16 -v 4 APK_NAME.apkבודקים את האפליקציה ביסודיות, ומתמקדים באזורים שעשויים להיות מושפעים משינוי מופעי קוד שמפנים לגודלי דפים ספציפיים.
הגדרה של אמולטור Android עם קובץ אימג' של המערכת שמבוסס על 16KB
כדי להגדיר סביבה של 16 KB באמצעות אמולטור Android, מבצעים את השלבים הבאים:
- ב-Android Studio, לוחצים על Tools > SDK Manager (כלים > SDK Manager).
בכרטיסייה SDK Platforms, לוחצים על Show Package Details, מרחיבים את הקטע Android VanillaIceCream או גרסה חדשה יותר ובוחרים אחת או שתיים מתמונות המערכת הבאות של האמולטור, בהתאם למכשירים הווירטואליים שרוצים ליצור:
- תמונת מערכת של Google APIs Experimental בגודל דף של 16 KB ARM 64 v8a
- Google APIs Experimental 16 KB Page Size Intel x86_64 Atom System Image
לוחצים על Apply (החלה) > OK כדי להוריד את תמונות המערכת שבחרתם.
פועלים לפי השלבים כדי להגדיר מכשיר וירטואלי ל-Android 15, וכשמוצגת בקשה לבחור תמונת מערכת, בוחרים את תמונת המערכת בגודל 16KB שהורדתם. אם היא לא מומלצת באופן אוטומטי, אתם יכולים למצוא את קובץ האימג' של המערכת בגודל 16KB בכרטיסייה Other Images.
הפעלת האמולטור
אחרי שמסיימים להגדיר את אמולטור Android ואת המכשירים הווירטואליים, מפעילים את האמולטור מתפריט מכשיר היעד או משורת הפקודה.
הפעלת מצב 16 KB במכשיר באמצעות אפשרויות למפתחים
כדי להפעיל מכשיר במצב 16KB, מעבירים את המתג של אפשרות המפתחים הפעלה בגודל דף של 16KB.
בגרסאות QPR של Android 15, אפשר להשתמש באפשרות למפתחים שזמינה במכשירים מסוימים כדי להפעיל את המכשיר במצב 16KB ולבצע בדיקות במכשיר. לפני שמשתמשים באפשרות למפתחים, עוברים אל הגדרות > מערכת > עדכוני תוכנה ומחילים את כל העדכונים שזמינים.
אפשרות למפתחים זו זמינה במכשירים הבאים:
Pixel 8 ו-Pixel 8 Pro (עם Android 15 QPR1 ומעלה)
Pixel 8a (עם Android 15 QPR1 ומעלה)
Pixel 9, Pixel 9 Pro ו-Pixel 9 Pro XL (עם Android 15 QPR2 ומעלה)
Pixel 9a (עם Android מגרסה 16 ואילך)
מצב תאימות לאחור של 16KB
אזהרה במצב תאימות לגודל הדף
האפשרות לתאימות לאחור של 16KB זמינה כשהמכשיר פועל עם ליבה של 16KB. מנהל החבילות מריץ אפליקציה במצב תאימות לאחור של 16 KB כשמתקיימים התנאים הבאים:
- אם באפליקציה יש קובצי ELF (עם סיומת
.so) עם יישור מקטע LOAD של 4KB. - אם קובץ ה-APK הדחוס מכיל קובצי ELF לא דחוסים בגודל 4 KB עם יישור ZIP.
אם מנהל החבילות הפעיל מצב תאימות לאחור של 16 KB באפליקציה, תוצג באפליקציה אזהרה כשהיא תופעל בפעם הראשונה, שלפיה היא פועלת במצב תאימות לאחור של 16 KB.
מצב תאימות לאחור של 16KB מאפשר לחלק מהאפליקציות לפעול, אבל כדי להבטיח את המהימנות והיציבות הטובות ביותר, האפליקציות צריכות להיות מיושרות ל-16KB.
בדף פרטי האפליקציה, בקטע מתקדם, משנים את ההגדרה הפעלת האפליקציה במצב תאימות לגודל הדף כדי להפעיל או להשבית את מצב התאימות לאחור של 16KB לאפליקציה ספציפית. ההגדרה הזו מוצגת רק כשהמכשיר פועל עם גודל דף של 16KB.
הגדרה של מצב תאימות לגודל הדף
כדי להפעיל בכוח תאימות לאחור של 16 KB לכל אפליקציה במכשיר:
adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false
כדי להשבית את התאימות לאחור של 16KB בכל האפליקציות במכשיר:
adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true
ב-Android 17, אתם יכולים גם להשבית את התאימות לאחור של 16KB בכל אפליקציה, ולגרום לכל קובץ בינארי לא תואם להפסיק לפעול באופן מיידי:
adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
adb shell setprop pm.16kb.app_compat.disabled true
כדי להפעיל או להשבית את מצב התאימות לאחור באפליקציה ספציפית, מגדירים את המאפיין android:pageSizeCompat כ"מופעל" או "מושבת" בקובץ AndroidManifest.xml של האפליקציה. כשהמאפיין הזה מוגדר, האפליקציה לא מציגה אזהרות לגבי מצב תאימות לאחור כשהיא מופעלת.