Ce document décrit le contexte de conception, les types de stratégies et l'utilisation de la stratégie de cycle de vie du lecteur AliPlayerKit.
Contexte
La stratégie de cycle de vie du lecteur est un modèle de conception abstrait à partir du pool de lecteurs multi-instances initialement mis en œuvre pour les applications de courts métrages d'Alibaba Cloud. Cette conception permet un défilement vidéo rapide en utilisant un pool d'instances de lecteur partagé globalement pour offrir les avantages suivants :
Réutilisation des instances : réduit la surcharge liée à la création de nouvelles instances.
Configuration dynamique : contrôle flexible du nombre d'instances.
Optimisation des threads : offre un contrôle fin des ressources thread.
Cette approche garantit un chargement rapide de la première image, une transition fluide et une empreinte mémoire maîtrisée, ce qui la rend adaptable à une large gamme de scénarios métier.
Concepts clés
Qu'est-ce qu'une stratégie de cycle de vie du lecteur ?
Une stratégie de cycle de vie du lecteur est une architecture permettant de gérer le cycle de vie des instances de lecteur. Elle définit les opérations fondamentales telles que l'acquisition, le recyclage et la suppression, découplant ainsi la gestion des ressources du lecteur de la logique métier.
Les différents scénarios métier ont des exigences distinctes en matière de gestion des instances de lecteur :
|
Cas d'utilisation |
Exigences |
Stratégie recommandée |
|
Lecture vidéo standard |
Simple et directe, aucune réutilisation nécessaire. |
default strategy |
|
Liste de vidéos courtes |
Changements fréquents, nécessite un démarrage rapide. |
reuse pool strategy |
|
Préchargement vidéo |
L'instance est liée à un ID et prend en charge le préchargement. |
ID-scoped strategy |
|
Scénarios sensibles à la mémoire |
Une seule instance globale pour une empreinte mémoire minimale. |
singleton strategy |
Avantages du modèle de stratégie
Le modèle de stratégie offre les avantages principaux suivants pour la gestion du cycle de vie du lecteur :
Découplage : sépare la logique métier des détails de création et de destruction des instances de lecteur.
Flexibilité : permet de changer de stratégie au moment de l'exécution sans modifier la logique métier.
Extensibilité : vous pouvez implémenter des stratégies personnalisées pour répondre à des besoins métier spécifiques.
Observabilité : des événements de cycle de vie unifiés facilitent la surveillance et le débogage.
Fonctionnalités
Problèmes résolus
Gestion décentralisée : absence de mécanisme unifié pour contrôler les instances de lecteur.
Surcharge des performances : la création et la destruction fréquentes des instances de lecteur gaspillent des ressources.
Difficultés de réutilisation : les instances de lecteur ne peuvent pas être réutilisées dans différents scénarios.
Mémoire non contrôlée : manque de contrôle efficace sur l'empreinte mémoire.
Valeur principale
|
Utilisation |
Description |
Avantage |
|
default strategy |
Crée une nouvelle instance pour chaque utilisation et la détruit ensuite. |
Simple et directe, sans état résiduel. |
|
reuse pool strategy |
Maintient un pool d'objets pour réutiliser les instances inactives. |
Réduit la surcharge de création et améliore la vitesse de démarrage. |
|
ID-scoped strategy |
Maintient une instance distincte pour chaque ID. |
Prend en charge le préchargement et la réutilisation liée à l'ID. |
|
singleton strategy |
Une seule instance globale. |
Empreinte mémoire minimale. |
Avantages architecturaux :
Découplage des stratégies : la gestion des ressources du lecteur est séparée de la logique métier pour une responsabilité claire.
Commutation au moment de l'exécution : changez de stratégie sans redémarrer l'application.
Sécurité des threads : toutes les implémentations de stratégie prennent en charge un accès sécurisé depuis plusieurs threads.
Piloté par les événements : publie des événements de cycle de vie via un bus d'événements pour faciliter la surveillance.
Capacités principales
|
Capacité |
Description |
|
Acquisition d'instance |
acquire() Acquiert une instance de lecteur. La stratégie détermine s'il faut créer une nouvelle instance ou en réutiliser une existante. |
|
Recyclage d'instance |
recycle() Recycle une instance de lecteur. La stratégie détermine s'il faut la détruire ou la conserver. |
|
Nettoyage des ressources |
clear() Nettoie toutes les ressources, garantissant une libération sécurisée. |
|
Préchargement |
preload() Crée des instances à l'avance pour réduire le temps jusqu'à la première image. |
|
Contrôle de la capacité du pool |
setMaxPoolSize() Ajuste dynamiquement la capacité du pool. |
Composants intégrés
Types de stratégies
AliPlayerKit fournit quatre stratégies de cycle de vie du lecteur intégrées :
|
Stratégie |
Description |
Cas d'utilisation |
Empreinte mémoire |
|
DefaultLifecycleStrategy |
La stratégie par défaut. Crée une nouvelle instance pour chaque utilisation et la détruit immédiatement après. |
Scénarios de lecture standard, simple et directe. |
Faible |
|
ReusePoolLifecycleStrategy |
La stratégie de pool de réutilisation. Maintient un pool inactif et utilise une politique LIFO (Last-In, First-Out) pour la réutilisation. |
Listes de vidéos courtes et flux d'informations. |
Moyenne |
|
IdScopedPoolLifecycleStrategy |
La stratégie étendue par ID. Maintient une instance distincte pour chaque ID et utilise une politique LRU (Least Recently Used) pour l'éviction. |
Préchargement vidéo, commutation multi-vidéo. |
Moyenne |
|
SingletonLifecycleStrategy |
La stratégie singleton. Une seule instance globale. |
Scénarios sensibles à la mémoire, lecture vidéo unique. |
La plus faible |
Détails des stratégies
DefaultLifecycleStrategy (stratégie par défaut)
Il s'agit de la stratégie la plus simple. Chaque appel à acquire() crée une nouvelle instance, et chaque appel à recycle() la détruit immédiatement.
Points clés :
Sans état, fournissant une nouvelle instance à chaque fois.
Aucune réutilisation, adaptée aux scénarios simples.
Empreinte mémoire la plus faible (les ressources sont libérées immédiatement après utilisation).
ReusePoolLifecycleStrategy (stratégie de pool de réutilisation)
Basée sur le modèle de pool d'objets, cette stratégie maintient un pool inactif et une liste active. Elle utilise une politique LIFO (Last-In, First-Out) pour réutiliser les instances.
Points clés :
Politique LIFO pour le pool inactif, où l'instance utilisée le plus récemment est réutilisée en premier.
Prend en charge le préchargement pour créer des instances à l'avance.
Capacité du pool configurable. Les instances sont détruites si la capacité est dépassée.
Idéale pour les listes de vidéos courtes et les scénarios de flux d'informations.
Chaque instance de lecteur consomme environ 35 à 40 Mo de mémoire. La capacité par défaut du pool est de 3, que vous pouvez ajuster en fonction des performances de l'appareil.
IdScopedPoolLifecycleStrategy (stratégie étendue par ID)
Maintient une instance de lecteur distincte pour chaque uniqueId, renvoyant toujours la même instance pour un ID donné. Elle utilise un mécanisme LRU (Least Recently Used) pour contrôler le nombre d'instances.
Points clés :
Liée à l'ID, réutilise toujours la même instance pour le même ID.
Le mécanisme d'éviction LRU nettoie automatiquement les instances utilisées le moins récemment.
Prend en charge le préchargement pour créer des instances non liées à l'avance.
Idéale pour le préchargement vidéo et les scénarios de commutation multi-vidéo.
SingletonLifecycleStrategy (stratégie singleton)
Maintient une seule instance de lecteur globale. Elle renvoie toujours la même instance, quel que soit le uniqueId.
Points clés :
Unique globalement, tous les appelants partagent la même instance.
Empreinte mémoire la plus faible.
Non adaptée à la lecture simultanée de plusieurs vidéos.
Événements de cycle de vie
La stratégie publie des événements de cycle de vie auxquels vous pouvez vous abonner sur le bus d'événements :
|
Événement |
Description |
Déclenchement |
|
PlayerCreated |
Un lecteur est créé. |
Lorsqu'une nouvelle instance est créée. |
|
PlayerDestroyed |
Un lecteur est détruit. |
Lorsqu'une instance est détruite. |
|
PlayerReused |
Un lecteur est réutilisé. |
Lorsqu'une instance est récupérée du pool pour réutilisation. |
|
PlayerHit |
Une instance de lecteur est trouvée. |
Lorsqu'une instance existante est trouvée pour un ID donné. |
|
PlayerEvicted |
Un lecteur est évincé. |
Lorsqu'une instance est évincée par la politique LRU ou parce que le pool est plein. |
Utilisation de base
Utiliser la stratégie par défaut
C'est la manière la plus simple d'utiliser le lecteur, sans configuration supplémentaire nécessaire :
// Create a controller (automatically uses DefaultLifecycleStrategy)
AliPlayerController controller = new AliPlayerController(context);
// Bind the player
AliPlayerModel model = new AliPlayerModel.Builder()
.videoSource(videoSource)
.build();
controller.configure(model);
playerView.attach(controller);
Utiliser la stratégie de pool de réutilisation
Cette stratégie est idéale pour les listes de vidéos courtes et les scénarios de flux d'informations :
// Get the singleton instance of the reuse pool strategy
ReusePoolLifecycleStrategy strategy = ReusePoolLifecycleStrategy.getInstance();
// Set the pool capacity (optional, defaults to 3)
strategy.setMaxPoolSize(3);
// Inject the strategy when creating the controller
AliPlayerController controller = new AliPlayerController(context, strategy);
// Clean up resources when finished
strategy.clear();
Utiliser la stratégie étendue par ID
Cette stratégie est idéale pour les scénarios nécessitant un préchargement et une commutation entre plusieurs vidéos :
// Get the singleton instance of the ID-scoped strategy
IdScopedPoolLifecycleStrategy strategy = IdScopedPoolLifecycleStrategy.getInstance();
// Set the pool capacity
strategy.setMaxPoolSize(3);
// Preload player instances (optional)
strategy.preload(context, 2);
// Inject the strategy when creating the controller
AliPlayerController controller = new AliPlayerController(context, strategy);
// Clean up resources when finished
strategy.clear();
Utiliser la stratégie singleton
Cette stratégie est idéale pour les scénarios sensibles à la mémoire :
// Get the singleton strategy instance
SingletonLifecycleStrategy strategy = SingletonLifecycleStrategy.getInstance();
// Preload the instance (optional, only creates one instance)
strategy.preload(context, 1);
// Inject the strategy when creating the controller
AliPlayerController controller = new AliPlayerController(context, strategy);
Utilisation avancée
Écoute des événements de cycle de vie
Écoutez les événements de cycle de vie internes d'une stratégie via le bus d'événements pour le débogage et la surveillance :
PlayerEventBus eventBus = PlayerEventBus.getInstance();
// Listen for player creation
eventBus.subscribe(PlayerLifecycleEvents.PlayerCreated.class, event -> {
Log.d("Player", "Created: " + event.playerId);
});
// Listen for player reuse
eventBus.subscribe(PlayerLifecycleEvents.PlayerReused.class, event -> {
Log.d("Player", "Reused: " + event.playerId);
});
// Listen for player eviction
eventBus.subscribe(PlayerLifecycleEvents.PlayerEvicted.class, event -> {
Log.d("Player", "Evicted: " + event.playerId);
});
// Listen for player destruction
eventBus.subscribe(PlayerLifecycleEvents.PlayerDestroyed.class, event -> {
Log.d("Player", "Destroyed: " + event.playerId);
});
// Unsubscribe when no longer needed
eventBus.unsubscribe(PlayerLifecycleEvents.PlayerCreated.class, listener);
Commutation dynamique des stratégies
Vous pouvez commuter les stratégies au moment de l'exécution en fonction des besoins métier :
private IPlayerLifecycleStrategy mCurrentStrategy;
private void switchToReusePool() {
// 1. Clean up old resources
if (mCurrentStrategy != null) {
mCurrentStrategy.clear();
}
// 2. Switch to the reuse pool strategy
mCurrentStrategy = ReusePoolLifecycleStrategy.getInstance();
mCurrentStrategy.setMaxPoolSize(3);
// 3. Preload
mCurrentStrategy.preload(this, 2);
}
private void switchToSingleton() {
// 1. Clean up old resources
if (mCurrentStrategy != null) {
mCurrentStrategy.clear();
}
// 2. Switch to the singleton strategy
mCurrentStrategy = SingletonLifecycleStrategy.getInstance();
}
Préchargement des instances de lecteur
Le préchargement crée des instances de lecteur à l'avance pour réduire le temps jusqu'à la première image :
// Reuse pool strategy: Preloaded instances are placed in the idle pool
ReusePoolLifecycleStrategy strategy = ReusePoolLifecycleStrategy.getInstance();
strategy.preload(context, 2); // Pre-create 2 instances// ID-scoped strategy: Preloads unbound instances
IdScopedPoolLifecycleStrategy strategy = IdScopedPoolLifecycleStrategy.getInstance();
strategy.preload(context, 2); // Pre-create 2 unbound instances// Singleton strategy: Preloads the global instance
SingletonLifecycleStrategy strategy = SingletonLifecycleStrategy.getInstance();
strategy.preload(context, 1); // Creates the global instance (the count parameter is ignored)
Implémentation d'une stratégie personnalisée
Implémentez une stratégie personnalisée en étendant BaseLifecycleStrategy :
public class MyCustomStrategy extends BaseLifecycleStrategy {
private final Map<String, IMediaPlayer> playerMap = new HashMap<>();
@NonNull@Overridepublic IMediaPlayer acquire(@NonNull Context context, @NonNull String uniqueId) {
// Custom acquisition logic
IMediaPlayer player = playerMap.get(uniqueId);
if (player != null) {
// Hit an existing instance
PlayerEventBus.getInstance().post(
new PlayerLifecycleEvents.PlayerHit(player.getPlayerId()));
return player;
}
// Create a new instance
player = createPlayer(context);
playerMap.put(uniqueId, player);
return player;
}
@Overridepublic void recycle(@Nullable IMediaPlayer player, @NonNull String uniqueId, boolean force) {
if (player == null) return;
if (force) {
// Force destruction
playerMap.remove(uniqueId);
destroyPlayer(player);
} else {
// Only stop the player, keep the instance
player.stop();
}
}
@Overridepublic void clear() {
// Clean up all instances
for (IMediaPlayer player : playerMap.values()) {
destroyPlayer(player);
}
playerMap.clear();
}
}
Bonnes pratiques
Sélection de la stratégie
|
Cas d'utilisation |
Stratégie recommandée |
Description |
|
Lecture vidéo standard |
default strategy |
Simple et directe, sans surcharge supplémentaire. |
|
Liste de vidéos courtes (style TikTok) |
reuse pool strategy |
Réutilise les instances pour une commutation rapide. |
|
Préchargement vidéo |
ID-scoped strategy |
Lie les instances aux IDs et prend en charge le préchargement. |
|
Appareils bas de gamme |
singleton strategy |
Empreinte mémoire minimale. |
|
Vidéos éducatives (commutation multi-vidéo) |
ID-scoped strategy |
Précharge la vidéo suivante. |
Optimisation de la mémoire
Chaque instance de lecteur consomme environ 35 à 40 Mo de mémoire (selon les résultats de profilage mémoire du pool de lecteurs multi-instances d'Alibaba Cloud pour les courts métrages). Nous vous recommandons d'ajuster la capacité du pool en fonction des performances de l'appareil :
|
Type d'appareil |
Configuration recommandée |
Capacité du pool |
|
Appareil haut de gamme |
ReusePool ou IdScopedPool |
3 |
|
Appareil milieu de gamme |
ReusePool ou IdScopedPool |
2 |
|
Appareil bas de gamme |
Singleton |
1 (par défaut) |
|
Scénarios sensibles à la mémoire |
Singleton |
1 |
// Dynamically adjust based on device memory
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowMemory = am.isLowRamDevice();
if (isLowMemory) {
// Use the singleton strategy on low-memory devices
strategy = SingletonLifecycleStrategy.getInstance();
} else {
// Use the reuse pool strategy on standard devices
strategy = ReusePoolLifecycleStrategy.getInstance();
strategy.setMaxPoolSize(isHighEndDevice ? 3 : 2);
}
Précautions
|
Élément |
Description |
|
Nettoyage rapide |
Appelez clear() ou recycle(force=true) pour libérer les ressources lorsqu'une page est détruite. |
|
Singletons de stratégie |
Les classes ReusePoolLifecycleStrategy, IdScopedPoolLifecycleStrategy et SingletonLifecycleStrategy sont implémentées comme des singletons avec un état partagé globalement. |
|
Sécurité des threads |
Toutes les implémentations de stratégie sont thread-safe et peuvent être utilisées dans des environnements multi-threads. |
|
Désabonnement aux événements |
Pour éviter une fuite de mémoire, désabonnez-vous toujours des événements de cycle de vie au moment approprié. |
|
Ordre d'initialisation |
Les stratégies sont initialisées automatiquement. Vous n'avez pas besoin d'appeler init() manuellement. |
Exemple
Un exemple complet est fourni dans le projet à l'emplacement playerkit-examples/example-lifecycle-strategy.
Fonctionnalités de l'exemple
|
Fonctionnalité |
Description |
|
Commutation de stratégie |
Commutez dynamiquement entre les quatre stratégies intégrées. |
|
Affichage de l'état |
Affiche l'état en temps réel de la stratégie (créé/réutilisé/trouvé/évincé). |
|
Écoute des événements |
Écoute et affiche les événements de cycle de vie. |
|
Lecture multi-vidéo |
Démontre l'effet de la commutation entre plusieurs vidéos avec différentes stratégies. |
Exécuter l'exemple
Dans l'application de démonstration, sélectionnez l'exemple Lifecycle Strategy pour le voir en action.
API
Interfaces principales
|
Interface/classe |
Description |
|
|
L'interface de stratégie de cycle de vie du lecteur, qui définit les opérations principales. |
|
|
La classe de base des stratégies, qui encapsule la logique commune. |
|
|
L'interface de fabrique de lecteur, responsable de la création et de la destruction des instances. |
Méthodes IPlayerLifecycleStrategy
|
Méthode |
Description |
|
|
Initialise la stratégie et injecte la fabrique de lecteur. |
|
|
Acquiert une instance de lecteur. |
|
|
Recycle une instance de lecteur. |
|
|
Nettoie toutes les ressources. |
|
|
Précharge des instances de lecteur. |
Méthodes BaseLifecycleStrategy
|
Méthode |
Description |
|
|
Définit la capacité maximale du pool (effectif uniquement pour les stratégies basées sur un pool). |
|
|
Crée une instance de lecteur (appelée par les sous-classes). |
|
|
Détruit une instance de lecteur (appelée par les sous-classes). |
Fonctionnement
Sécurité des threads
|
Stratégie |
Sécurité des threads |
|
DefaultLifecycleStrategy |
Sans état et thread-safe. |
|
ReusePoolLifecycleStrategy |
Blocs synchronized. |
|
IdScopedPoolLifecycleStrategy |
Blocs synchronized. |
|
SingletonLifecycleStrategy |
AtomicReference + verrouillage à double vérification. |
Comparaison des stratégies de mise en pool
|
Fonctionnalité |
ReusePool |
IdScopedPool |
|
Mécanisme de réutilisation |
LIFO (Last-In, First-Out) |
Liaison ID + LRU |
|
Prise en charge du préchargement |
Oui (place les instances dans le pool inactif) |
Oui (place les instances dans une file d'attente non liée) |
|
Politique d'éviction |
Évince lorsque le pool est plein |
Éviction LRU automatique |
|
Cas d'utilisation |
Liste de vidéos courtes |
Préchargement multi-vidéo |
Logique architecturale sous-jacente :
-
Pourquoi la stratégie de pool de réutilisation utilise-t-elle LIFO (Last-In, First-Out) ?
Dans les scénarios de défilement vertical fréquent dans un flux, l'instance de lecteur qui vient d'être ajoutée au pool inactif est la plus « chaude » en termes de contexte de décodeur sous-jacent et de ressources matérielles (son cache n'a pas encore été paginé par le système). La réutiliser en premier réduit considérablement le temps nécessaire aux réinitialisations de contexte de bas niveau, améliorant ainsi la vitesse de démarrage selon le principe de localité spatiale.
-
Pourquoi la stratégie étendue par ID utilise-t-elle LRU (Least Recently Used) ?
La mise en pool multi-instances suit le principe de localité temporelle : lorsqu'un utilisateur fait défiler un flux, la probabilité de rejouer une vidéo beaucoup plus ancienne diminue. La politique d'éviction LRU s'aligne sur ce schéma de décroissance de l'historique de navigation de l'utilisateur, permettant un contrôle efficace de l'empreinte mémoire de pointe de l'application tout en maintenant une liaison stricte aux IDs.
FAQ
Choisir la bonne stratégie
Choisissez en fonction de votre scénario métier :
Lecture simple : utilisez la stratégie par défaut pour une complexité nulle.
Vidéos courtes/flux d'informations : utilisez la stratégie de pool de réutilisation pour une commutation rapide.
Préchargement vidéo : utilisez la stratégie étendue par ID pour la liaison ID.
Scénarios sensibles à la mémoire : utilisez la stratégie singleton pour l'empreinte la plus petite.
Définir la capacité du pool
Ajustez la capacité en fonction des performances de l'appareil et des besoins métier :
Chaque instance de lecteur consomme environ 35 à 40 Mo de mémoire.
La capacité par défaut du pool est de 3, ce qui consomme environ 100 à 120 Mo.
Pour les appareils bas de gamme, définissez la capacité à 2 ou utilisez la stratégie singleton.
Utiliser le préchargement
Utilisez le préchargement dans les scénarios où vous devez réduire le temps jusqu'à la première image :
Liste de vidéos courtes : préchargez 1 à 2 instances.
Page de détail vidéo : préchargez la vidéo recommandée suivante.
Évitez un préchargement excessif, car il augmente l'utilisation de la mémoire.
Pièges courants
Les situations suivantes sont les causes les plus courantes des problèmes signalés par les utilisateurs. Assurez-vous de les éviter :
Exemple 1 : Oubli d'appeler clear()
Code incorrect :
@Overrideprotected void onDestroy() {
super.onDestroy();
// Only detached the View, forgot to clean up instances held by the strategy
mPlayerView.detach();
}
Code correct :
@Overrideprotected void onDestroy() {
super.onDestroy();
// Detach the View
mPlayerView.detach();
// Clean up strategy resources
if (mStrategy != null) {
mStrategy.clear();
}
}
Cause : la stratégie (comme ReusePool) conserve des références aux instances de lecteur. Ne pas les nettoyer entraînera une fuite de mémoire.
Exemple 2 : Échec de la désinscription d'un écouteur d'événements
Code incorrect :
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Subscribed to an event but never unsubscribed
PlayerEventBus.getInstance().subscribe(
PlayerLifecycleEvents.PlayerCreated.class,
event -> updateUI()
);
}
Code correct :
private PlayerEventBus.EventListener<PlayerLifecycleEvents.PlayerCreated> mListener;
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListener = event -> updateUI();
PlayerEventBus.getInstance().subscribe(
PlayerLifecycleEvents.PlayerCreated.class, mListener);
}
@Overrideprotected void onDestroy() {
super.onDestroy();
// Unsubscribe if (mListener != null) {
PlayerEventBus.getInstance().unsubscribe(
PlayerLifecycleEvents.PlayerCreated.class, mListener);
}
}
Cause : le bus d'événements conserve une référence à l'Activity. Si l'écouteur n'est pas supprimé, l'Activity ne peut pas être libérée après sa destruction.
Exemple 3 : Utilisation d'une instance de lecteur après son recyclage
Code incorrect :
// Recycle the player
strategy.recycle(player, "video_1", false);
// Continue to use the player after recycling
player.start(); // May cause an exception
Code correct :
// Recycle the player
strategy.recycle(player, "video_1", false);
player = null; // Clear the reference to prevent misuse. Re-acquire it when needed.
player = strategy.acquire(context, "video_1");
Cause : après le recyclage d'une instance de lecteur, celle-ci peut être arrêtée ou détruite. Son utilisation ultérieure provoquera une exception.
Débogage
Vérifiez les journaux : filtrez Logcat en utilisant
tag:AliPlayerKit.Écoutez les événements de cycle de vie : observez les événements de création, de réutilisation et d'éviction sur le bus d'événements.
Vérifiez l'état du pool : consultez la taille actuelle du pool et le nombre d'instances actives via les journaux.
Analysez la mémoire : utilisez Android Profiler pour vérifier le nombre d'instances de lecteur et leur empreinte mémoire.