OVH Cloud OVH Cloud

free

13 réponses
Avatar
Sylfelin
Bonjour,

Développeur delphi depuis Delphi 3 je découvre c#.

Une choses m'intrique dans les cours que je lis.
Soit par exemple :

String[] MyList = new String[];

Dans aucun des exemples il y a une instruction du style MyList.Free;


C'est le garbace collection de dotnet qui se charge de libérer la
mémoire ?

Par soucis de proprété (et par habitude) peut'on forcer cette
libération ?

Merci




--
---
Sylfelin

3 réponses

1 2
Avatar
Merlin
> Et c'est pour çà qu'il s'appellent des finaliseurs, et pas des
destructeurs;-)



Chipotons : le rôle d'un destructeur dans un langage non managé tel que
C++ ou Delphi sous Win32 est à la fois de libérer la mémoire de
l'instance autant que de libérer les ressources occupées par celle-ci
autre que de la mémoire.
On notera que la libération de la mémoire de l'instance est malgré tout
automatisée et qu'il n'y a pas besoin explicitement de la relacher dans
le destructeur. Mais c'est bien dans le destructeur que cela a lieu.
C'est le principe de destructeur déterministe.

Sous .NET la libération de mémoire des instances est automatisée par le
GC.
Mais il existe une méthode, Finalize, héritée de System.Object,
protégée et virtuelle, qui peut être surchargée pour libérer des
ressources.
Le langage C# implémente bien la notion de destructeur (qui se note
avec un tilde suivi du nom de la classe), ce qui n'a rien à voir avec
la méthode Finalize qui est le fameux "finaliseur" et qui est propre à
.NET et non au langage C# lui-même qui pourrait être implémenté sous
d'autres OS.
Il s'avère que le processus de libération de mémoire de .NET est non
déterministe. Donc le destructeur et le finaliseur peuvent ne pas être
appelé.
Et comme le destructeur n'a plus la charge de libérer la mémoire sous
.NET, ça fait que son rôle devient réellement mineur.

Mais C# implémente bien la notion de destructeur, avec une syntaxe
particulière.
C'est .NET et non C# en tant que langage, qui fait que le destructeur
joue un rôle mineur doublé par le finaliseur qui est non déterministe
par dessus le marché.
D'où le mécanisme supplémentaire de IDisposable, les artifices comme
les méthodes Close() sur les stream fichiers, etc.


Le seul langage .NET qui implémente les destructeurs, c'est C++/CLI grâce à
la "stack semantic".



C++ managé est une horreur soit dit en passant.

Malheureusement, appeler çà avec le même nom qu'un autre concept proche mais
subtilement et fondamentalement différent dans un langage extrêmement connu
(C++ natif), c'est une source de confusions sans fin qui ne se justifie que
par une accroche commerciale de bas étage.



On pourrait aussi dire que nier que C# implémente des destructeurs
uniquement parce que leur rôle se trouve affaibli sous .NET est, à mon
avis, une erreur de jugement.
Mais que cela ne te gache pas ta journée... :-)

--

///3rL1n____
Avatar
Cyrille Szymanski
"Merlin" wrote in
news::

Chipotons : le rôle d'un destructeur dans un langage non managé tel
que C++ ou Delphi sous Win32 est à la fois de libérer la mémoire de
l'instance autant que de libérer les ressources occupées par celle-ci
autre que de la mémoire.



En C++ ce n'est pas dans le destructeur que la mémoire est libérée. Tu peux
très bien explicitement appeler le destructeur 20 fois et la mémoire n'en
sera pas libérée pour autant.

On notera que la libération de la mémoire de l'instance est malgré
tout automatisée et qu'il n'y a pas besoin explicitement de la
relacher dans le destructeur. Mais c'est bien dans le destructeur que
cela a lieu. C'est le principe de destructeur déterministe.



C'est au contraire lors de la libération de la mémoire que le destructeur
est appelé (juste avant) et pas l'inverse.

Une application pratique : les appels explicites au destructeur, couplés à
l'instanciation en place et une surcharge de l'opérateur new permettent de
créer un pool d'objets.

--
Cyrille Szymanski
Avatar
Arnaud Debaene
Merlin wrote:
Et c'est pour çà qu'il s'appellent des finaliseurs, et pas des
destructeurs;-)



Chipotons : le rôle d'un destructeur dans un langage non managé tel
que C++ ou Delphi sous Win32 est à la fois de libérer la mémoire de
l'instance autant que de libérer les ressources occupées par celle-ci
autre que de la mémoire.
On notera que la libération de la mémoire de l'instance est malgré
tout automatisée et qu'il n'y a pas besoin explicitement de la
relacher dans le destructeur. Mais c'est bien dans le destructeur que
cela a lieu. C'est le principe de destructeur déterministe.



Raté : Ce n'est pas le destructeur qui libère la mémoire, c'est le runtime
lors de *l'expression delete* si l'objet est sur le tas. Si l'objet est sur
la pile, la mémoire est libérée par l'épilogue de la fonction courante.

Si tu veux te convaincre de la nuance, regardes du côté du "new avec
placement" dans ton bouquin favori de C++ (natif).

Sous .NET la libération de mémoire des instances est automatisée par
le GC.
Mais il existe une méthode, Finalize, héritée de System.Object,
protégée et virtuelle, qui peut être surchargée pour libérer des
ressources.
Le langage C# implémente bien la notion de destructeur (qui se note
avec un tilde suivi du nom de la classe), ce qui n'a rien à voir avec
la méthode Finalize qui est le fameux "finaliseur" et qui est propre à
.NET et non au langage C# lui-même qui pourrait être implémenté sous
d'autres OS.



Re-raté : Le "destructeur" C# est traduit en méthode Finalize à la
compilation!

Exemple :
Code source C# :

public class maclasse
{
~maclasse()
{
System.Console.WriteLine("dans le finaliseur");
}
}

Compiles çà puis ouvre l'assembly dans ildasm ou .NET Reflector : la
traduction en IL de la classe est la suivante (avec VS2005, en release ) :

.class public auto ansi beforefieldinit maclasse
extends object
{
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: call instance void object::.ctor()
L_0006: ret
}

.method family hidebysig virtual instance void Finalize() cil managed
{
.maxstack 1
L_0000: ldstr "dans le finaliseur"
L_0005: call void [mscorlib]System.Console::WriteLine(string)
L_000a: leave.s L_0013
L_000c: ldarg.0
L_000d: call instance void object::Finalize()
L_0012: endfinally
L_0013: ret
.try L_0000 to L_000c finally handler L_000c to L_0013

}
}

Sans entrer dans le détail, on voit facilement que le compilateur a généré
un constructeur sans paramètres, et a redéfini la méthode virtuelle Finalize
avec le code correspondant au "destructeur" C# + un appel à Object.Finalize.


Il s'avère que le processus de libération de mémoire de .NET est non
déterministe.


Oui, et généralement on s'en fiche. Que la libération des ressources autres
que la mémoire soit non déterministe, voilà le vrai problème.

Donc le destructeur et le finaliseur peuvent ne pas être
appelé.
Et comme le destructeur n'a plus la charge de libérer la mémoire sous
.NET, ça fait que son rôle devient réellement mineur.


Je dirais ca différemment : du fait des contraintes imposées par le GC sur
le finaliseur, il est quasiment impossible d'en faire quelque chose d'utile.


Mais C# implémente bien la notion de destructeur, avec une syntaxe
particulière.


Non : Il implémente la notion de Finaliseur (càd la méthode Finalize, qui
sera *éventuellement* appelée *à un certain moment* par le GC avant de
libérer la mémoire de l'objet). Ce n'est pas la notion classique de
destructeur telle qu'on la définit traditionnellement en modélisation objet.

Le seul langage .NET qui implémente les destructeurs, c'est C++/CLI
grâce à la "stack semantic".



C++ managé est une horreur soit dit en passant.



Je parles de C++/CLI (version VC2005) qui a beaucoup amélioré la syntaxe, et
qui propose une vrai notion de destructeur : pour faire simple, le
"destructeur" au niveau code source est traduit en méthode Dispose dans l'IL
généré.

Malheureusement, appeler çà avec le même nom qu'un autre concept
proche mais subtilement et fondamentalement différent dans un
langage extrêmement connu (C++ natif), c'est une source de
confusions sans fin qui ne se justifie que par une accroche
commerciale de bas étage.



On pourrait aussi dire que nier que C# implémente des destructeurs
uniquement parce que leur rôle se trouve affaibli sous .NET est, à mon
avis, une erreur de jugement.
Mais que cela ne te gache pas ta journée... :-)



C++/CLI a bien réussi à implémenter la sémantique correcte des destructeurs
*ET* des finaliseurs en .NET, donc C# aurait pu en faire tout autant.

Si ca n'a pas été fait, je pense que c'est pour des raisons de simplicité et
parce que ces concepts sont peut être considérés comme "trop avancés" pour
un langage qui se veut large public comme C#. C'est une erreur AMHA....

Arnaud
MVP - VC
1 2