Renvoyer les pages de mémoire à l'OS

La libération d'un objet dans le code de votre application ou la fermeture d'un flux temporaire ne renvoie pas toujours la RAM physique au système d'exploitation. Les allocateurs natifs personnalisés, les environnements d'exécution avec récupération de mémoire et les tables de hachage à adressage ouvert conservent fréquemment des arènes de mémoire virtuelle qui restent sauvegardées par des pages physiques longtemps après la disparition des données logiques qu'elles contiennent. De même, la lecture ou le mappage en mémoire d'archives d'assets volumineuses remplit le cache de pages du noyau, qui reste imputé au groupe de contrôle de la mémoire (memcg) de votre application.

Cette page explique comment utiliser les appels système madvise et posix_fadvise pour renvoyer les pages anonymes et sauvegardées inutilisées à l'OS tout en conservant les réservations d'adresses virtuelles et les descripteurs de fichiers intacts, et comment vérifier l'impact dans adb shell et Perfetto.

Pourquoi la désallocation logique conserve la RAM physique

Le kernel Linux suit la mémoire physique au niveau de précision des pages matérielles, qui sont de 4 ko ou 16 ko sur les appareils Android. Lorsqu'un processus réserve une région d'espace d'adressage virtuel avec mmap, le noyau crée une zone de mémoire virtuelle (VMA) sans allouer de RAM physique. Le noyau n'alloue et ne facture une page physique que lorsque le processus touche cette page pour la première fois. Pour la mémoire anonyme, une première lecture mappe une page zéro partagée. Par conséquent, seule une écriture alloue de la RAM.

Une fois qu'une page est chargée dans la RAM, le kernel ne sait pas si les octets qu'elle contient représentent toujours des objets actifs dans l'environnement d'exécution de votre application. Deux modèles courants permettent de conserver les pages physiques après que votre application a fini de les utiliser :

  • Arènes et pools anonymes : les allocateurs natifs personnalisés (tels que les allocateurs de slab, de pool ou d'arène de frame), les environnements d'exécution gérés avec des espaces de tas non mobiles et les tables de hachage volumineuses réservent des plages d'adresses virtuelles contiguës avec mmap à l'aide de MAP_PRIVATE | MAP_ANONYMOUS. Lorsque votre application réinitialise une arène, libère un bloc dans un pool personnalisé ou efface une table de hachage en mettant à zéro ses métadonnées, les pages sous-jacentes restent modifiées. Sans indication explicite au kernel, ces pages continuent d'être comptabilisées dans la taille de l'ensemble résident (RSS) et la taille de l'ensemble proportionnel (PSS) anonymes de votre processus, ainsi que dans sa charge cgroup memory.current. En cas de pression sur la mémoire, le noyau les compresse dans la ZRAM, où elles occupent toujours l'espace d'échange facturé à memory.swap.current.
  • Cache de pages basé sur des fichiers à partir du streaming d'éléments : lorsque votre application lit des bundles d'éléments volumineux, des archives de niveau, des conteneurs multimédias ou des modèles de machine learning à l'aide de read, pread ou de mmap basé sur des fichiers, le noyau met en cache chaque page lue dans le cache de pages et impute ces pages au compteur file dans le fichier memory.stat du cgroup de votre processus. Si votre application décompresse ou copie cet élément dans un tampon d'exécution anonyme ou l'importe dans le GPU, le fait de conserver les pages de fichiers bruts dans le cache de pages duplique l'élément dans la RAM.
Pages d'arène modifiées publiées avec madvise et pages d'assets mises en cache supprimées avec posix_fadvise, tandis que la plage virtuelle et le descripteur de fichier restent intacts.
Figure 1. Récupération des pages physiques des arènes virtuelles et du cache de pages.

Libérer les pages anonymes

L'appel système madvise donne au noyau des conseils sur la façon dont votre processus prévoit d'utiliser une plage d'adresses virtuelles. Pour les mappages anonymes privés (MAP_PRIVATE | MAP_ANONYMOUS), la transmission de MADV_DONTNEED indique au noyau de supprimer immédiatement les pages physiques et toutes les entrées ZRAM échangées qui sauvegardent la plage spécifiée alignée sur la page.

Après madvise avec MADV_DONTNEED, renvoie :

  • La plage d'adresses virtuelles reste réservée et valide dans l'espace d'adressage de votre processus (VmSize ne change pas). Les pointeurs et les limites de l'arène restent donc intacts sans appeler munmap ni mmap.
  • Les pages physiques sont immédiatement dissociées et libérées, ce qui réduit RssAnon, Pss, le compteur anon dans memory.stat et memory.swap.current pour toutes les pages qui se trouvaient dans la ZRAM.
  • Toute lecture ou écriture ultérieure à une adresse de cette plage entraîne une erreur sur une nouvelle page remplie de zéros à la demande.
Indicateur de conseil Mappage de la cible Comportement du kernel sur Android
MADV_DONTNEED Anonyme privé (MAP_PRIVATE | MAP_ANONYMOUS) Supprime immédiatement les pages physiques et les emplacements d'échange ZRAM. Les accès suivants entraînent des erreurs dans les pages remplies de zéros. Baisse déterministe de RssAnon et memory.current.
MADV_DONTNEED Sauvegardé dans un fichier (MAP_PRIVATE ou MAP_SHARED) Annule le mappage des pages des tables de pages du processus (suppressions RssFile). Les pages privées de copie sur écriture d'un mappage MAP_PRIVATE sont supprimées. Les modifications sont donc perdues et l'accès suivant relit le fichier. Les pages MAP_SHARED modifiées sont réécrites dans l'espace de stockage ultérieurement. Les pages propres restent dans le cache de pages jusqu'à ce qu'elles soient supprimées.
MADV_FREE Anonyme privé (MAP_PRIVATE | MAP_ANONYMOUS) Marque les pages comme réutilisables de manière différée. Le noyau conserve les pages dans RssAnon si aucune pression de mémoire n'existe et ne les récupère que lorsque la récupération globale ou memcg est exécutée.
MADV_WILLNEED Anonyme sauvegardé dans un fichier ou échangé Démarre la lecture anticipée asynchrone pour les mappages de fichiers ou lit les pages anonymes échangées depuis la ZRAM dans le cache d'échange à l'avance.

Aligner les plages intérieures sur la taille de page de l'environnement d'exécution

madvise fonctionne sur des pages entières. Elle nécessite une adresse de début alignée sur la page et arrondit la longueur à la limite de la page suivante. Si vous libérez un segment dans une arène partagée où les octets voisins contiennent toujours des objets actifs, l'arrondi de la plage vers l'extérieur peut annuler les allocations actives qui partagent les pages limites.

Interrogez toujours la taille de page d'exécution avec sysconf(_SC_PAGESIZE) pour prendre en charge les appareils avec des pages de 4 Ko et de 16 Ko, et arrondissez l'adresse de début intérieure à la limite de page suivante et l'adresse de fin intérieure à la limite de page précédente. Pour en savoir plus sur les allocateurs indépendants de la taille de page, consultez Optimiser les allocateurs de mémoire personnalisés.

#include <sys/mman.h>
#include <unistd.h>
#include <cstdint>

// Reclaims physical pages strictly inside [region_start, region_start + length)
// without disturbing live objects on partially occupied boundary pages.
void ReleaseUnusedArenaSpan(void* region_start, size_t length) {
  const uintptr_t page_size = static_cast<uintptr_t>(sysconf(_SC_PAGESIZE));
  const uintptr_t page_mask = ~(page_size - 1);

  const uintptr_t raw_start = reinterpret_cast<uintptr_t>(region_start);
  const uintptr_t raw_end = raw_start + length;

  // Round start UP and end DOWN so only fully unused pages are discarded.
  const uintptr_t aligned_start = (raw_start + page_size - 1) & page_mask;
  const uintptr_t aligned_end = raw_end & page_mask;

  if (aligned_end > aligned_start) {
    madvise(reinterpret_cast<void*>(aligned_start),
            aligned_end - aligned_start,
            MADV_DONTNEED);
  }
}

Lorsque vous créez des mappages anonymes personnalisés avec mmap, ajoutez l'étiquette prctl à la VMA à l'aide de PR_SET_VMA_ANON_NAME afin de pouvoir suivre sa résidence par nom dans showmap et /proc/<pid>/smaps. dumpsys meminfo compte les régions anonymes portant un nom personnalisé sous Unknown. Certains anciens noyaux conservent un pointeur vers le nom au lieu de le copier. Transmettez donc une chaîne qui reste valide pendant la durée de vie du mappage, telle qu'une chaîne littérale :

#include <sys/mman.h>
#include <sys/prctl.h>

#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#endif

void* ReserveNamedArena(size_t capacity_bytes, const char* arena_name) {
  void* ptr = mmap(nullptr, capacity_bytes, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (ptr != MAP_FAILED) {
    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
          reinterpret_cast<unsigned long>(ptr), capacity_bytes,
          reinterpret_cast<unsigned long>(arena_name));
  }
  return ptr;
}

Évincer les pages du cache de fichiers

Lorsque votre application lit des données à partir d'un descripteur de fichier, le noyau met en cache les pages du fichier dans le cache de pages en prévision des lectures futures. Bien que cela accélère les accès répétés aux petits fichiers de configuration ou aux bibliothèques partagées, cela peut vous pénaliser lors de la diffusion d'archives d'assets volumineuses, de packages de textures ou de fragments audio et vidéo qui sont analysés une fois et stockés sous forme décodée ailleurs.

L'appel système posix_fadvise permet à votre application d'informer le noyau du modèle d'accès aux fichiers attendu sur un descripteur de fichier ouvert :

  • POSIX_FADV_DONTNEED : indique au noyau que la plage d'octets spécifiée ne sera pas accessible dans un avenir proche. Le noyau supprime immédiatement les pages mises en cache propres et non mappées dans cette plage alignée sur les pages du cache de pages, ce qui réduit le compteur file dans memory.stat et la charge memory.current de votre cgroup.
  • POSIX_FADV_SEQUENTIAL : double la fenêtre de lecture anticipée du noyau pour le descripteur de fichier, ce qui améliore le débit lors de la diffusion d'une archive volumineuse du début à la fin. Associez-le à POSIX_FADV_DONTNEED sur les fragments de fin à mesure que vous les consommez, afin que l'empreinte du cache de la page reste limitée à une petite fenêtre glissante.
  • POSIX_FADV_RANDOM : désactive la lecture anticipée spéculative du noyau sur le descripteur de fichier. Utilisez cette option lorsque vous effectuez des recherches éparses et non contiguës par décalage d'octets dans un fichier d'archive ou de base de données volumineux, afin que chaque petite lecture n'intègre pas les pages inutilisées adjacentes dans le cache de pages. Pour les fichiers mappés en mémoire, appelez plutôt madvise avec MADV_RANDOM sur le mappage.

L'exemple suivant diffuse une archive d'assets volumineuse par fragments, ce qui permet d'activer la lecture anticipée séquentielle et d'évincer du cache de pages chaque plage alignée sur la page traitée :

#include <fcntl.h>
#include <unistd.h>
#include <cstdint>
#include <vector>

void StreamAssetArchive(int fd, size_t total_size) {
  const size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
  constexpr size_t kChunkSize = 4 * 1024 * 1024;  // 4 MB streaming window
  std::vector<uint8_t> buffer(kChunkSize);

  // Hint sequential access so the kernel prefetches upcoming chunks.
  posix_fadvise(fd, 0, total_size, POSIX_FADV_SEQUENTIAL);

  off_t offset = 0;
  while (offset < static_cast<off_t>(total_size)) {
    ssize_t bytes_read = pread(fd, buffer.data(), buffer.size(), offset);
    if (bytes_read <= 0) break;

    // Decode or upload buffer[0 .. bytes_read) into runtime structures here.

    // Evict full pages that have been completely consumed.
    const size_t aligned_len =
        (static_cast<size_t>(bytes_read) / page_size) * page_size;
    if (aligned_len > 0) {
      posix_fadvise(fd, offset, static_cast<off_t>(aligned_len),
                    POSIX_FADV_DONTNEED);
    }
    offset += bytes_read;
  }
}

Récupérer les régions mmap sauvegardées par des fichiers

Si votre application mappe en mémoire un fichier d'asset volumineux avec mmap au lieu de le lire avec pread, l'appel de posix_fadvise avec POSIX_FADV_DONTNEED seul n'expulse pas les pages qui sont toujours mappées dans les tables de pages de votre processus. Pour supprimer un intervalle consommé d'un mappage de fichier en lecture seule sans annuler le mappage de l'intégralité de la VMA :

  1. Appelez madvise(addr, length, MADV_DONTNEED) sur la plage d'adresses virtuelles mappée pour annuler le mappage de ces pages à partir des tables de pages de votre processus (RssFile drops).
  2. Appelez posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED) sur le décalage de fichier correspondant afin que le noyau supprime également les pages propres non mappées du cache de pages (file dans les suppressions memory.stat).

Suivre les consignes concernant les conseils sur les pages

Appliquez ces pratiques lorsque vous intégrez madvise et posix_fadvise dans les allocateurs, les chargeurs d'assets et les gestionnaires de cycle de vie :

  • Appels par lot dans des régions approximatives : chaque appel madvise prend le verrou de mappage de mémoire de votre processus (mmap_lock), parcourt les tables de pages et invalide les entrées de tampon de traduction (TLB) correspondantes sur d'autres cœurs de processeur (avec des instructions de maintenance TLB diffusées sur Arm et des interruptions interprocesseurs sur x86). N'appelez pas madvise pour chaque petite désallocation. Fusionnez les plages libres et conseillez le noyau par blocs de 64 Ko à 1 Mo ou plus, ou balayez les plages libres pendant les limites de frame et les transitions en arrière-plan (onStop et TRIM_MEMORY_UI_HIDDEN).
  • Récupérer la capacité vide dans les tables de hachage et les pools : lorsqu'une table de hachage, un pool d'objets ou un tampon de préproduction volumineux est effacé après une transition de scène ou une synchronisation de données, réduisez son allocation de sauvegarde ou émettez MADV_DONTNEED sur le tableau de sauvegarde effacé. Le fait de conserver une table à adressage ouvert de 32 Mo allouée avec des pierres tombales conserve l'intégralité des 32 Mo de pages physiques facturées à votre processus.
  • Vider les données de fichiers modifiées avant de les supprimer : POSIX_FADV_DONTNEED ne supprime que les pages de cache de pages propres. Si votre application écrit un fichier temporaire ou de cache volumineux qu'elle ne lira pas immédiatement, appelez fdatasync avant posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) afin que les pages modifiées soient écrites dans l'espace de stockage, puis supprimées de la RAM.
  • N'évincez pas le code ou les éléments partagés actifs : évitez d'appeler POSIX_FADV_DONTNEED sur les fichiers DEX, ODEX, VDEX, les bibliothèques partagées (.so) ou les éléments d'interface utilisateur auxquels vous accédez de manière répétée. L'éviction des pages de code ou d'éléments actifs force les erreurs de stockage synchrones qui bloquent les threads et augmentent la pression d'E/S, comme indiqué par les informations sur les blocages de pression (PSI).

Vérifier la récupération de pages dans le shell

Vérifiez que madvise et posix_fadvise récupèrent de la RAM physique à l'aide de showmap et des fichiers de comptabilité cgroup v2 de votre application.

Inspecter la taille virtuelle par rapport à la taille physique avec showmap

Lorsque vous nommez une arène anonyme avec PR_SET_VMA_ANON_NAME, showmap affiche à la fois sa réservation virtuelle (virtual size) et ses pages physiques résidentes (RSS et PSS). L'exemple de résultat suivant provient de l'application exemple MemoryLab dans l'AOSP, qui nomme son arène memorylab-arena. Remplacez com.example.app et memorylab-arena par le nom de votre package et le nom de l'arène. Avant d'appeler madvise avec MADV_DONTNEED sur une arène de 64 Mo touchée, exécutez :

adb shell "showmap \$(pidof com.example.app) | grep -E '^ +virtual|^ +size|memorylab-arena' | head -3"
 virtual                     shared   shared  private  private                   Anon      Shmem     File      Shared   Private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked    # object
   65536    65536    65536        0        0        0    65536        0        0         0         0         0        0        0        0    1 [anon:memorylab-arena]

Après avoir appelé madvise(arena_ptr, 64 * 1024 * 1024, MADV_DONTNEED), exécutez à nouveau showmap :

 virtual                     shared   shared  private  private                   Anon      Shmem     File      Shared   Private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked    # object
   65536        0        0        0        0        0        0        0        0         0         0         0        0        0        0    1 [anon:memorylab-arena]

La valeur virtual size reste à 65 536 Ko (64 Mo), ce qui confirme que la plage d'adresses virtuelles est toujours réservée, tandis que RSS, PSS et private dirty passent à 0 Ko.

Inspecter memory.current et memory.stat du cgroup

Pour vérifier à la fois la récupération de pages anonymes et l'éviction du cache de pages non mappées (qui n'apparaît pas dans les tables de pages de processus si elle est lue avec pread), inspectez le répertoire cgroup v2 du processus. Sur Android, chaque processus d'application possède son propre cgroup (par exemple, /sys/fs/cgroup/apps/uid_<UID>/pid_<PID>/, selon l'appareil). La commande suivante recherche le répertoire à partir de la ligne 0:: de /proc/<PID>/cgroup et affiche les compteurs :

adb shell '
  PID=$(pidof com.example.app)
  CG=/sys/fs/cgroup$(sed -n "s/^0:://p" /proc/$PID/cgroup)
  echo "memory.current: $(cat $CG/memory.current)"
  echo "memory.swap.current: $(cat $CG/memory.swap.current)"
  grep -E "^(anon|file) " $CG/memory.stat
'

Avant de libérer une zone anonyme de 64 Mo et d'expulser un fichier d'asset mappé en mémoire de 128 Mo du cache de pages :

memory.current: 242962432
memory.swap.current: 16633856
anon 100671488
file 140009472

Immédiatement après avoir appelé madvise avec MADV_DONTNEED sur l'arène et sur le mappage du fichier d'asset, puis posix_fadvise avec POSIX_FADV_DONTNEED sur le descripteur de fichier d'asset :

memory.current: 41766912
memory.swap.current: 16502784
anon 33722368
file 5914624

anon diminue de 64 Mo et file de 128 Mo, ce qui réduit memory.current d'environ 192 Mo sans fermer le descripteur de fichier ni annuler le mappage de l'espace d'adressage virtuel de l'arène. memory.swap.current change à peine, car presque aucune des pages de l'arène n'a été transférée vers la ZRAM. Vos valeurs dépendent de l'appareil et de la quantité de fichiers que le kernel a déjà supprimée de lui-même. Pour en savoir plus sur la façon dont le noyau facture et récupère la mémoire par cgroup, consultez Récupération de mémoire : éviction et échange.

Analyser la récupération de pages dans Perfetto

Perfetto vous permet de mettre en corrélation les appels madvise et posix_fadvise avec les compteurs de table de pages et de cache de pages du noyau sur une chronologie unifiée.

Enregistrer une trace

Capturez une trace avec linux.process_stats pour les compteurs de processus périodiques et linux.ftrace pour les événements RSS et de cache de page en temps réel, puis transférez-la sur votre ordinateur :

adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/page_advice.perfetto-trace <<EOF
buffers: {
    size_kb: 65536
    fill_policy: RING_BUFFER
}
data_sources: {
    config {
        name: "linux.process_stats"
        process_stats_config {
            scan_all_processes_on_start: true
            proc_stats_poll_ms: 100
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "kmem/rss_stat"
            ftrace_events: "filemap/mm_filemap_add_to_page_cache"
            ftrace_events: "filemap/mm_filemap_delete_from_page_cache"
        }
    }
}
duration_ms: 20000
EOF
adb pull /data/misc/perfetto-traces/page_advice.perfetto-trace

Inspecter les pistes dans l'UI Perfetto

Ouvrez la trace dans l'interface utilisateur de Perfetto, développez le groupe de processus de votre application et épinglez les pistes de compteur suivantes pour qu'elles restent en haut de la chronologie :

  • mem.virt : reste stable sur les appels MADV_DONTNEED, ce qui confirme que la réservation d'adresse virtuelle (mmap) n'est pas modifiée.
  • mem.rss.anon : diminue fortement au code temporel exact où votre application appelle madvise avec MADV_DONTNEED sur des zones de mémoire anonymes ou des régions de tas modifiées.
  • mem.rss.file : diminue lorsque votre application appelle madvise avec MADV_DONTNEED sur une région mmap basée sur un fichier, accompagnée d'une rafale d'événements ftrace mm_filemap_delete_from_page_cache lorsque posix_fadvise avec POSIX_FADV_DONTNEED supprime les pages propres du cache de pages.
  • mem.swap : diminue lorsque MADV_DONTNEED supprime des pages anonymes qui avaient été compressées dans la ZRAM.
Traces de compteur Perfetto pour un processus. mem.rss.anon et mem.rss.file augmentent lorsqu'une arène et un fichier d'asset sont renseignés, puis diminuent après madvise et posix_fadvise, tandis que mem.swap et mem.virt restent stables.
Figure 2 : RSS dropping after page advice calls.

La figure 2 montre ces pistes pour l'application exemple MemoryLab. Pour explorer la même vue, ouvrez l'exemple de trace dans l'UI Perfetto.

Interroger la mémoire maximale et finale avec PerfettoSQL

Exécutez la requête PerfettoSQL suivante dans l'interface utilisateur de Perfetto (onglet Query (SQL)) pour comparer les valeurs maximales et finales de mem.virt, mem.rss.anon, mem.rss.file et mem.swap pour votre processus cible :

WITH counters AS (
  SELECT t.name AS counter_name, c.ts, c.value
  FROM counter c
  JOIN process_counter_track t ON c.track_id = t.id
  JOIN process p USING (upid)
  WHERE p.name = 'com.example.app'
    AND t.name IN ('mem.virt', 'mem.rss.anon', 'mem.rss.file', 'mem.swap')
)
SELECT
  counter_name,
  CAST(MAX(value) / 1048576 AS INT) AS peak_mb,
  CAST((
    SELECT value FROM counters AS last
    WHERE last.counter_name = counters.counter_name
    ORDER BY ts DESC LIMIT 1
  ) / 1048576 AS INT) AS final_mb
FROM counters
GROUP BY counter_name
ORDER BY counter_name;

Exemple de résultat pour la trace de la figure 2 :

counter_name  peak_mb  final_mb
mem.rss.anon      111        48
mem.rss.file      212        84
mem.swap           36        36
mem.virt        13952     13952

mem.rss.anon diminue de 63 Mo et mem.rss.file de 128 Mo par rapport à leurs pics, tandis que mem.virt se termine à son pic, car les réservations virtuelles sont inchangées.

Résoudre les pages expulsées en fichiers

Les événements mm_filemap_add_to_page_cache et mm_filemap_delete_from_page_cache identifient les fichiers par numéro d'inode (i_ino) plutôt que par chemin d'accès. Pour confirmer que les pages expulsées appartiennent au fichier que vous avez indiqué, comptez les expulsions par inode :

SELECT
  EXTRACT_ARG(arg_set_id, 'i_ino') AS inode,
  COUNT(*) AS evicted_pages
FROM ftrace_event
WHERE name = 'mm_filemap_delete_from_page_cache'
GROUP BY inode
ORDER BY evicted_pages DESC
LIMIT 5;

Exemple de résultat pour la trace de la figure 2 :

inode     evicted_pages
9761              35781
76792192            767
126                 668
0                   612
11587712            514

Pendant que l'appareil qui a enregistré la trace est toujours connecté, résolvez chaque inode en un chemin d'accès. Les numéros d'inode ne sont uniques que dans un seul système de fichiers. Limitez donc la recherche au système de fichiers qui contient les données de votre application :

INODE=<inode from the query>
adb shell "find /data -xdev -inum $INODE 2>/dev/null"

Exemple de résultat pour l'inode 9761 :

/data/user/0/com.android.memorylab/files/asset_pack.bin
/data/data/com.android.memorylab/files/asset_pack.bin

Les deux chemins d'accès font référence au même fichier, car /data/user/0 et /data/data sont deux noms pour le même répertoire de données d'application. Le nombre d'évictions pour le fichier d'élément (35 781 pages) est supérieur à celui du fichier lui-même (32 768 pages à 4 Ko), car kswapd avait déjà évincé une partie du fichier sous pression de la mémoire avant que l'application ne le lise et n'appelle posix_fadvise.

Pour obtenir un script qui résout de nombreux inodes à la fois, consultez Résoudre les erreurs liées aux inodes, puis aux fichiers.


← Interaction entre kswapd et lmkd | ↑ Haut | Processus en arrière-plan léger →