Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Nombre de references

26 réponses
Avatar
Delf
Bonsoir.

Y a t-il un moyen de savoir si un objet est référencé, ou bien
connaître le nombre de pointeurs sur l'objet en question ?

Pour expliqué, j'ai une classe faisant office de Cache, un thread est
lancé pour supprimer périodiquement les objets expirés.

Problème d'entrelacement si je récupère un objet, il n'est pas 'null',
le thread l'efface juste après ce teste et je le manipule par la
suite...

Je ne souhaite pas utiliser .Clone(), et puis les objets du Cache
n'implémentent pas forcément IClonable.

Merci.

10 réponses

1 2 3
Avatar
Jérémy Jeanson
Bonjour Delf,

Pourquoi ne pas simplement déclencher le garbage collector?
Tu te complique peut être un peu la vie, en méttant te objet à null
après un petit dispose et ne déclenchant l'ami GC, tu dois avoir une
libération de tes ressources propres (testé sur de grosses application,
ça marche bien).

De manière générale: il suffit de bien se conduire quand on code, après
usage d'un objet Dispose() (si il dispose d'un dispose() :)) puis null.
--
Jérémy JEANSON
MCP
http://www.jjeanson.fr
Avatar
Christophe Lephay
"Delf" a écrit dans le message de groupe de discussion :
493abb2e$0$27405$
Bonsoir.

Y a t-il un moyen de savoir si un objet est référencé, ou bien connaître
le nombre de pointeurs sur l'objet en question ?

Pour expliqué, j'ai une classe faisant office de Cache, un thread est
lancé pour supprimer périodiquement les objets expirés.

Problème d'entrelacement si je récupère un objet, il n'est pas 'null', le
thread l'efface juste après ce teste et je le manipule par la suite...

Je ne souhaite pas utiliser .Clone(), et puis les objets du Cache
n'implémentent pas forcément IClonable.



Une solution possible consiste à sérialiser tes objets en mémoire. Conserver
les données de l'objet dans ton cache sous forme de MemoryStream t'éviterait
de référencer l'objet lui-même.

La contrainte d'un tel système est que tes objets mis en cache doivent être
sérialisables, mais c'est une contrainte bien moins lourde que celle
consistant à devoir implémenter IClonable. en effet, si l'objet n'est pas en
lui-même sérialisable, tu peux tout de même le sérialiser en définissant une
nouvelle classe SurrogateSelector.

Dans tous les cas, il est possible que la solution à ton problème soit
complexe, ou même qu'il n'existe pas de solution. Le mieux serait, je pense,
que tu donnes un peu plus de détails sur ce que tu veux faire et pourquoi.
Avatar
Delf
Dans son message précédent, Delf a écrit :

[...]



Bizarre, je vois des posts sous Google Group mais non pas via Free...

Lorsque mon thread traite chaque élément du Cache, il regarde si l'item
implémente IDisposable, si oui, il invoque la méthode Dispose().
Finalement, il Remove() l'item de la collection. Le tout, en section
critique.

Mon problème est dû à un entrelacement particulier.

1. Côté code, je récupère un pointeur sur mon item du Cache :

object o = CacheManager.Get("monObjet");

if (o != null)
{
// entrelacement à ce niveau-là...

L'objet n'est pas expiré, o est donc différent de 'null'.

2. Un instant après, le thread "purgatoire" se réveille, analyse l'item
"monObjet" ; à ce moment là, il peut être expiré : il est effacé de la
collection.

3. Côté code, je fais

// dans le if précédent...

Console.WriteLine(o.ToString());
}

L'item n'existant plus, o vaut null, NullReferenceException...

Ce que je voudrais faire : lorsque le thread parcourt la collection,
pour chaque item, savoir s'il est référencé ailleurs.

Genre : if (o != null && o.ReferenceNumber != 0) ...

Une personne parlait d'utiliser un SerializerSugotruc (je ne connais
pas).

J'ai trouvé sur le MSDN une source d'exemple que j'ai testée. J'ai
notamment relevé une propriété sur le stream : Position qui change
quand le stream est lu.

Je pourrais surement m'en servir pour savoir si l'objet a 'été
référencé' avant que le thread ne soit lancé (repositionné par le
thread à 0 à la première boucle ; à la seconde, si valeur = 0 alors on
peut effacer)

Mais ça me paraît lourd comme méthode...
Avatar
Christophe Lephay
"Delf" a écrit dans le message de groupe de discussion :
493d62f4$0$17079$
Dans son message précédent, Delf a écrit :

[...]



Bizarre, je vois des posts sous Google Group mais non pas via Free...

Lorsque mon thread traite chaque élément du Cache, il regarde si l'item
implémente IDisposable, si oui, il invoque la méthode Dispose().
Finalement, il Remove() l'item de la collection. Le tout, en section
critique.

Mon problème est dû à un entrelacement particulier.

1. Côté code, je récupère un pointeur sur mon item du Cache :

object o = CacheManager.Get("monObjet");

if (o != null)
{
// entrelacement à ce niveau-là...

L'objet n'est pas expiré, o est donc différent de 'null'.

2. Un instant après, le thread "purgatoire" se réveille, analyse l'item
"monObjet" ; à ce moment là, il peut être expiré : il est effacé de la
collection.

3. Côté code, je fais

// dans le if précédent...

Console.WriteLine(o.ToString());
}

L'item n'existant plus, o vaut null, NullReferenceException...

Ce que je voudrais faire : lorsque le thread parcourt la collection, pour
chaque item, savoir s'il est référencé ailleurs.

Genre : if (o != null && o.ReferenceNumber != 0) ...

Une personne parlait d'utiliser un SerializerSugotruc (je ne connais pas).

J'ai trouvé sur le MSDN une source d'exemple que j'ai testée. J'ai
notamment relevé une propriété sur le stream : Position qui change quand
le stream est lu.



La position n'est pas un problème : tu aurais un MemoryStream par objet
caché, qui serait lu du début à la fin pour le reconstruire.

Je pourrais surement m'en servir pour savoir si l'objet a 'été référencé'
avant que le thread ne soit lancé (repositionné par le thread à 0 à la
première boucle ; à la seconde, si valeur = 0 alors on peut effacer)

Mais ça me paraît lourd comme méthode...



A vrai dire, j'ai l'impression que tu t'engages dans une mauvaise direction
(voire même une voie sans issue).

Quels sont les types d'objets que tu veux gérer dans ton cache, et pourquoi
?

J'ai l'impression que tu veux pouvoir mettre en cache tout type d'objet, ce
qui est non seulement inefficace mais aussi simplement impossible vu que :
1- .net ne fournit apparemment aucun moyen de connaitre le nombre de
références ;
2- l'opérateur d'affectation ne peut pas être surchargé, t'empêchant ainsi
d'implémenter ton propre comptage de références.

Sans savoir ce que tu veux faire de manière plus globale, c'est difficile
d'être affirmatif sur quoi que ce soit.
Avatar
Arnaud Lhopiteau
Bonjour,

Je suppose que le problême exposé par notre ami est un exercice donné
dans le cadre de ses études, ai je tort?

je pense qu'il faut d'abord rappeler quelques point:

Le Garbage Collector:
Lorsqu'il y a besoin de libérer de la mémoire le garbage collector
de .Net recherche les objets orphelins en mémoire, c'est à dire non
référencé (je vais à l'essentiel et vous fait grâce de la notion de
génération dans mes explications).
Comment détecte t'il qu'un objet est orphelin?
Les variables et propriétés référençant des objets définis par des
classes sont en fait des "références". Le Garbage Collector recense
donc tous les objets en mémoire qui ne sont pas la cible de ces
références et les désigne comme orphelins.

L'interface IDispose:
Cette interface à pour rôle de mettre à disposition un mécanisme d e
libération des ressources, typiquement handles de fichier, connections
à une base de données...
Le résultat de IDispose n'est en aucun cas la libération de la
mémoire occupée par l'objet lui même pour lequel on appelle IDispose.
Si on garde une référence sur un objet sur lequel on a utilisé
IDispose, le garbage collector ne le recyclera pas.

Les objets Streams:
les objets streams sont des objets d'accés "séquentiel" à des
données. La propriété "Position"désigne l'emplacement dans le strea m
où l'on va lire ou écrire de l'information, aucunement un compteur de
référencement.

Concernant le système de cache expliqué ici, il ne libérera pas la
mémoire des objets (je ne parle pas ici des sous-objets dont les
références pourraient être supprimées par la méthode Dispose)
référencés par le cache, sauf à certaines conditions:
1 * l'objet n'est pas référencé à l'extérieur du cache
2 * lorsque l'on veut supprimer l'objet on met sa référence à null
3 * on appelle le garbage collector (ce qui est généralement
déconseillé, il vaut mieux laisser le framework l'activer lui même
quand il le juge nécessaire)
4 * le garbage collector trouve utile de recycler l'objet

C'est donc trés aléatoire, qui plus est dangereux, car si un objet est
utilisé et que la méthode Dispose libère des ressources essentielles
au bon fonctionnement de cet objet...

Mais je connais une solution ,dont je serais heureux de discuter avec
toi si tu poses les bonnes questions...

Pour donner une piste utile je conseillerais de lire l'article
suivant:

http://msdn.microsoft.com/en-us/library/system.weakreference.aspx
(jIl y a un système de cache en exemple ;) )

Si tu peux mettre la mains sur le livre "Applied .Net Framework
Programming" de Jeffrey Richter, tu pourras aussi apprendre des choses
utiles.


Arnaud Lhopiteau
MCSD/MCTS
Avatar
Delf
Arnaud Lhopiteau vient de nous annoncer :

Je suppose que le problême exposé par notre ami est un exercice donné
dans le cadre de ses études, ai je tort?



Tord :)

Mon problème est que je n'attends pas le GC pour appeler .Dispose() sur
un objet qui expire via un DateTime.

Ici, expiration ne signifie pas libération des ressources.

je pense qu'il faut d'abord rappeler quelques point:

Le Garbage Collector:
Lorsqu'il y a besoin de libérer de la mémoire le garbage collector
de .Net recherche les objets orphelins en mémoire, c'est à dire non
référencé (je vais à l'essentiel et vous fait grâce de la notion de
génération dans mes explications).
Comment détecte t'il qu'un objet est orphelin?
Les variables et propriétés référençant des objets définis par des
classes sont en fait des "références". Le Garbage Collector recense
donc tous les objets en mémoire qui ne sont pas la cible de ces
références et les désigne comme orphelins.

L'interface IDispose:
Cette interface à pour rôle de mettre à disposition un mécanisme de
libération des ressources, typiquement handles de fichier, connections
à une base de données...
Le résultat de IDispose n'est en aucun cas la libération de la
mémoire occupée par l'objet lui même pour lequel on appelle IDispose.
Si on garde une référence sur un objet sur lequel on a utilisé
IDispose, le garbage collector ne le recyclera pas.



Je suis OK avec tout ce qui a été dit précédemment.

Les objets Streams:
les objets streams sont des objets d'accés "séquentiel" à des
données. La propriété "Position"désigne l'emplacement dans le stream
où l'on va lire ou écrire de l'information, aucunement un compteur de
référencement.



J'ai dû mal m'exprimer quand j'ai parlé de cette propriété.

Je voulais juste dire qu'avec sa valeur courante, je pouvais savoir si
l'objet avait été accédé. Je n'ai parlé à aucun moment de libération
mémoire via celui-ci.

Concernant le système de cache expliqué ici, il ne libérera pas la
mémoire des objets (je ne parle pas ici des sous-objets dont les
références pourraient être supprimées par la méthode Dispose)
référencés par le cache, sauf à certaines conditions:



Oui, c'est pour ça que je parle d'expiration.

1 * l'objet n'est pas référencé à l'extérieur du cache
2 * lorsque l'on veut supprimer l'objet on met sa référence à null



Ce que je fais.

3 * on appelle le garbage collector (ce qui est généralement
déconseillé, il vaut mieux laisser le framework l'activer lui même
quand il le juge nécessaire)



Oui.

4 * le garbage collector trouve utile de recycler l'objet
C'est donc trés aléatoire, qui plus est dangereux, car si un objet est
utilisé et que la méthode Dispose libère des ressources essentielles
au bon fonctionnement de cet objet...



Oui, tout à fait d'accord.

Mais je connais une solution ,dont je serais heureux de discuter avec
toi si tu poses les bonnes questions...



Mon problème est que le thread interne du Cache positionne les objets
expirés (via DateTime) à 'null' après avoir appelé la méthode
.Dispose() sur ceux-ci ; du moins pour ceux qui implémentent
l'interface. Ensuite, la collection des objets en Cache 'efface' ces
items expirés, le tout en section critique.

Après, je sais que c'est le GC qui va faire le boulot, toujours est-il
que mon objet, une fois récupéré, vaudra 'null'.

C'est pour cela qu'à un moment, entre le test de nullité de l'objet et
son état suite au passage du thread, en fonction de l'entrelacement des
process, l'objet peut être 'null' tout en ayant passé ce test-là dans
une autre partie du code (cf. exemple plus bas).

Je vais prendre un exemple tout bête : une boîte aux lettre, une lettre
dedans.

Je regarde, s'il y a une lettre, je *vais la prendre*.
Là, quelqu'un prends la lettre avant moi.
La boîte est maintenant vide, mais je suis toujours en train de plonger
la main dedans... et il n'y a plus de lettre -> je plante :)

Le problème est qu'une méthode du genre : object GetObjectByID(string
pID) ne me permet pas de verrouiller l'objet correspondant à l'ID, sauf
si j'utilise un Monitor + 2° méthode pour le releaser. Trop lourd.

De plus, ce n'est pas parce que je récupère un objet de mon Cache que
je vais l'utiliser instantanément, il peut s'écouler N secondes durant
lesquelles le thread peut passer et hop...

Pour donner une piste utile je conseillerais de lire l'article
suivant:

http://msdn.microsoft.com/en-us/library/system.weakreference.aspx
(jIl y a un système de cache en exemple ;) )



Déjà vu un exemple sur les Weakreferences sur CodeProject.

Si je ne me trompe pas, je ne peux pas utiliser de WeakRef dans mon cas
car mes objets ne doivent pas effacés par le GC avant une certaine
date/time d'expiration.

Si tu peux mettre la mains sur le livre "Applied .Net Framework
Programming" de Jeffrey Richter, tu pourras aussi apprendre des choses
utiles.



Connais pas.
Bien, je crois que dans ma précipitation de l'autre soir, je n'ai as
bien exprimé mes idées. Y a t-il d'autres points à éclaircir ?

Je récapitule avec du pseudocode :

(1) object obj = Cache.Get("obj_ID");

(2) if (obj != null)
{

(3)
(4) Console.WriteLine(obj.ToString());
}

(1)
Dans le Get(), si l'objet est toujours dans la collection, la méthode
regarde si l'objet est expiré : si c'est le cas, elle retourne 'null'
(même si l'objet en lui-même est toujours existant).

(2)
Je regarde si l'ojet est 'null', ce n'est pas le cas, je rentre dans le
bloc IF.

(3)
Le thread passe juste à cet instant, l'objet est maintenant expiré,
l'objet dans le Cache est positionné à 'null', je ne parle pas de
libération de ressource.

(4)
obj étant 'null', plantage...

Merci.

--
Delf
Avatar
Arnaud Lhopiteau
Si je comprends bien maintenant, le problême viens du fait que l'objet
peut être passé à "NULL" alors qu'il est à disposition à l'exté rieur
du cache?

Les objets mis à dispositions par ce cache peuvent ils être de tous
types, ou juste d'un certain nombre restreint de types, voir un seul?
Avatar
Delf
Arnaud Lhopiteau a exprimé avec précision :

Si je comprends bien maintenant, le problême viens du fait que l'objet
peut être passé à "NULL" alors qu'il est à disposition à l'extérieur
du cache?



Oui.

Les objets mis à dispositions par ce cache peuvent ils être de tous
types, ou juste d'un certain nombre restreint de types, voir un seul?



Pour l'instant je n'ai mis aucune limitation de type.
Est-ce une erreur ?

--
Delf
Avatar
Christophe Lephay
"Delf" a écrit dans le message de groupe de discussion :
49402f38$0$24524$
Arnaud Lhopiteau a exprimé avec précision :
Les objets mis à dispositions par ce cache peuvent ils être de tous
types, ou juste d'un certain nombre restreint de types, voir un seul?



Pour l'instant je n'ai mis aucune limitation de type.
Est-ce une erreur ?



Erreur, ou pas, tout dépend du but visé par ton cache. S'il s'agit
d'améliorer les performances, il me semble que le GC fera mieux dans la
plupart des cas.

Introduire des contraintes de type ou d'interface permettrait :

* d'augmenter les chances que le cache contribue bien à une amélioration des
performances, en évitant de cacher des classes dont on est certain que cela
produirait l'effet inverse ;

* de faire collaborer le cache avec les classes mises en cache, permettant
de mettre en oeuvre des stratégies plus spécifiques, à la fois plus simples,
plus efficaces et plus robustes.
Avatar
Arnaud Lhopiteau
On 10 déc, 22:07, Delf wrote:
Arnaud Lhopiteau a exprimé avec précision :

> Si je comprends bien maintenant, le problême viens du fait que l'obje t
> peut être passé à "NULL" alors qu'il est à disposition à l'ex térieur
> du cache?

Oui.

> Les objets mis à dispositions par ce cache peuvent ils être de tous
> types, ou juste d'un certain nombre restreint de types, voir un seul?

Pour l'instant je n'ai mis aucune limitation de type.
Est-ce une erreur ?

--
Delf



Le problême est, tel que je le conçois, que ta destruction d'objet ne
peut actuellement pas être retardé sur les objets en cours
d'utilisations.

Comment résoudre ce problême?

Il faudrait associer à chaque objet un verrou pour que si l'objet est
en cours d'utilisation, sa destruction soit retardée si nécessaire.

Ce verrou devrait être activé lorsque ton cache met a disposition un
objet, et désactivé lorsque cet objet n'est plus utilisé.

Si les objets utilisés avaient été d'un nombre restreint de classes,
il aurait été aisé de les "wrapper" par des objets implémentant le urs
interface et qui auraient géré automatiquement une incrémentation/
décrémentation d'un compteur (le verrou).

Dans le cas d'un nombre illimité de types, la solution qui me vient à
l'esprit, c'est de déclarer au conteneur (le cache) que l'on utilise
l'objet puis qu'on le libère.

Pour ce qui est de protéger l'objet, il faut le faire dés que le cache
cherche à le mettre à disposition (dans la méthode Get), donc avant
même de le "capter" (le prendre de son conteneur associatif),
incrémenter le compteur d'utilisation propre à cet objet, puis tester
la présence de l'objet, puis le mettre à disposition.

Pour "rendre" l'objet, il faudrait que ton cache implémente une
méthode qui prend en paramêtre la clé de l'objet et décrémente le
compteur correspondant.

Pour ce qui est des détails d'implémentation, on peut imaginer que les
compteurs d'utilisation sont des entiers contenus dans un conteneur
associatif (je te conseil une hashtable toute simple, les conteneurs
génériques ne sont pas thread safe). Surtout ne pas oublier de
verrouiller cette structure lorsque l'on y accède! (en faisant un lock
sur le SyncRoot).

Tout ceci nous ramène aux mêmes problêmes d'utilisation que connait l a
technologie COM/COM+/DCOM qui utilise des compteurs de référence pas
toujours mis à jour...

Arnaud Lhopiteau
MCSD/MCTS
1 2 3