תמיכה בגודלי דפים של 16 KB

בעבר, מערכת 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, לא תהיה לך אפשרות לפרסם אותם.

אזהרה ב-Google Play Console שלפיה עדכוני אפליקציות צריכים לתמוך בדפי זיכרון בגודל 16KB עד 1 בפברואר 2027
איור 1. אזהרת תאימות של Google Play Console.

היתרונות והשיפורים בביצועים

במכשירים שמוגדרים בהם דפים בגודל 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):

  1. פותחים את Android Studio, לוחצים על File > Open (קובץ > פתיחה) ובוחרים פרויקט כלשהו.
  2. בסרגל התפריטים, לוחצים על Build > Analyze APK...‎ (פיתוח > ניתוח APK...‎).

    אפשרות בתפריט Build ב-Studio להפעלת הכלי לניתוח APK
  3. בוחרים את ה-APK שרוצים לנתח.

  4. מחפשים בתיקייה lib, שמכילה קובצי אובייקטים משותפים (.so) אם יש כאלה. אם יש קובצי אובייקטים משותפים, האפליקציה משתמשת בקוד Native. בעמודה Alignment מוצגות אזהרות לגבי קבצים שיש בהם בעיות תאימות. אם אין קבצי אובייקט משותפים או אם אין תיקייה בשם lib, סימן שהאפליקציה לא משתמשת בקוד Native.

    תצוגת הכלי לניתוח APK שבה מוצגים קובצי אובייקט משותפים

זיהוי בעיות ביישור באמצעות בדיקות אוטומטיות

‫Android Studio מציג אזהרה באופן יזום אם הספריות או קובצי ה-APK המובנים מראש לא עומדים בדרישות של 16 KB. משתמשים בכלי לניתוח APK כדי לבדוק אילו ספריות צריך לעדכן או אם נדרשים שינויים בקוד.

התראות אזהרה ב-Studio לגבי בעיות תאימות בפרויקט

Lint ב-Android Studio גם מדגיש ספריות Native שלא מותאמות לדפים בגודל 16KB.

אזהרה של Studio linter לגבי ספריית Native לא מיושרת

בדיקת ההתאמה של מקטעי ELF לספריות משותפות

בספריות משותפות, צריך לוודא שפלח ה-ELF של הספריות המשותפות מיושר בצורה תקינה באמצעות יישור ELF בגודל 16 KB. אם אתם מפתחים ב-Linux או ב-macOS, אתם יכולים להשתמש בסקריפט check_elf_alignment.sh כמו שמתואר בקטע הבא. אפשר גם להשתמש ישירות בכלי שורת הפקודה.

שימוש בסקריפט check_elf_alignment.sh (ב-Linux או ב-macOS)

כדי לבדוק את היישור של מקטעי ELF באמצעות הסקריפט check_elf_alignment.sh, פועלים לפי השלבים הבאים:

  1. שומרים את הסקריפט check_elf_alignment.sh בקובץ.

  2. מריצים את הסקריפט בקובץ ה-APK של האפליקציה:

    check_elf_alignment.sh APK_NAME.apk
    

    הסקריפט מפיק את הפלט ALIGNED או UNALIGNED לכל הספריות המשותפות של arm64-v8a.

  3. אם יש ספריות משותפות מסוג arm64-v8a או x86_64 שהן UNALIGNED, צריך לעדכן את האריזה של הספריות האלה, ואז לקמפל מחדש את האפליקציה ולבדוק אותה מחדש לפי השלבים שמתוארים בקטע הזה.

שימוש ישיר בכלי שורת הפקודה

כדי לבדוק את ההתאמה של מקטעי ELF באמצעות כלי שורת הפקודה, פועלים לפי השלבים הבאים:

  1. מוודאים שגרסה 35.0.0 ואילך של Android SDK Build-Tools ו-Android NDK מותקנים באמצעות SDK Manager ב-Android Studio או באמצעות כלי שורת הפקודה sdkmanager.
  2. מחילוץ קובץ ה-APK של האפליקציה:

    Linux או macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    ‏Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. בספרייה הזמנית שחילצתם אליה את קובץ ה-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 LOAD
    

    ‏Windows (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
    
  4. בודקים את שורות הפלט כדי לוודא שאין ערכים קטנים מ-2**14 בקטעי הטעינה. אם יש קטעי טעינה עם הערכים 2**13, 2**12 או ערכים נמוכים יותר, תצטרכו לעדכן את האריזה של הספריות האלה, לקמפל מחדש את האפליקציה ולבדוק אותה שוב לפי השלבים בקטע הזה.

  5. לאחר מכן, מריצים את כלי שורת הפקודה zipalign בקובץ ה-APK של האפליקציה:

    Linux או macOS

    SDK_ROOT_LOCATION/Android/sdk/build-tools/35.0.0/zipalign -v -c -P 16 4 APK_NAME.apk
    

    ‏Windows (PowerShell)

    SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apk
    

    SDK_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:

  1. עדכון האריזה של הספריות המשותפות
  2. קומפילציה של האפליקציה באמצעות יישור ELF בגודל 16 KB
  3. תיקון קוד ופתרון בעיות בזמן ריצה
  4. אופטימיזציה של מקציבי זיכרון מותאמים אישית (אם רלוונטי)
  5. בדיקה אם ערכות ה-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, יכול להיות שיופיעו בה שגיאות אם יש מקומות בקוד שבהם מניחים שמכשיר מסוים משתמש בגודל דף ספציפי. כדי למנוע את זה, צריך לבצע את הפעולות הבאות:

  1. מסירים מהלוגיקה של הקוד תלות שמוגדרת בהארדקוד ומפנה לקבוע PAGE_SIZE או למופעים, שבהם מניחים שגודל הדף של המכשיר הוא 4 KB ‏ (4096).

    במקום זאת, צריך להשתמש ב-getpagesize() או ב-sysconf(_SC_PAGESIZE).

  2. חפשו שימושים ב-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 כדי לראות אם חוויית השימוש באפליקציה חווה רגרסיות. לשם כך, בצע את הצעדים הבאים:

  1. מגדירים את Android 15 SDK ואילך.

  2. מגדירים את אחת מסביבות הבדיקה הבאות:

  3. מפעילים את מכשיר הבדיקה ומריצים את הפקודה הבאה כדי לוודא שהוא משתמש בסביבה של 16KB:

    adb shell getconf PAGE_SIZE
    

    הפקודה אמורה להחזיר את הערך 16384.

  4. מריצים את הפקודה הבאה zipalign כדי לוודא שהאפליקציה מיושרת ל-16 KB, כאשר APK_NAME הוא שם קובץ ה-APK של האפליקציה:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. בודקים את האפליקציה ביסודיות, ומתמקדים באזורים שעשויים להיות מושפעים משינוי מופעי קוד שמפנים לגודלי דפים ספציפיים.

הגדרה של אמולטור Android עם קובץ אימג' של המערכת שמבוסס על 16KB

כדי להגדיר סביבה של 16 KB באמצעות אמולטור Android, מבצעים את השלבים הבאים:

  1. ב-Android Studio, לוחצים על Tools > ‏SDK Manager (כלים > SDK Manager).
  2. בכרטיסייה 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
    הורדת קובצי אימג' של מערכת האמולטור בגודל 16KB באמצעות SDK Manager ב-Android Studio
  3. לוחצים על Apply (החלה) > OK כדי להוריד את תמונות המערכת שבחרתם.

  4. פועלים לפי השלבים כדי להגדיר מכשיר וירטואלי ל-Android 15, וכשמוצגת בקשה לבחור תמונת מערכת, בוחרים את תמונת המערכת בגודל 16KB שהורדתם. אם היא לא מומלצת באופן אוטומטי, אתם יכולים למצוא את קובץ האימג' של המערכת בגודל 16KB בכרטיסייה Other Images.

    איתור תמונת האמולטור בגודל 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 של האפליקציה. כשהמאפיין הזה מוגדר, האפליקציה לא מציגה אזהרות לגבי מצב תאימות לאחור כשהיא מופעלת.