N'hésitez pas à partager nos tutos et codes sur vos réseaux sociaux, apportez-nous votre aide pour faire grandir notre communauté.
Solution :
Préambule
Je vais essayer de résumer certains cas que j'ai vus ou rencontrés, en quelques règles, qui, à mon avis, tiennent et expliquent la plupart ou la totalité des cas que je connais.
Les règles
Voici les règles (en supposant que $HistoryLength
est réglé sur 0
et qu'il n'y a pas d'éléments d'interface utilisateur présents à l'écran, ou d'autres constructions de code - comme par ex. Internal`Cache
objet que l'on pourrait utiliser, etc., qui font référence à l'un des symboles en question) :
-
Module
efface tout*Values
de symboles locaux, tant que toutes les conditions suivantes sont réunies :- a. Ils ne sont pas renvoyés de
Module
(par eux-mêmes ou en tant que parties d'expressions plus grandes). - b. Ils ne sont pas référencés par les définitions de tout symbole dans les portées lexicales extérieures, au moment où
Module
sort. - c. Ils n'ont pas de références circulaires les uns aux autres.
- a. Ils ne sont pas renvoyés de
-
Pour les variables locales n'ayant que
OwnValues
définies :- a. Si toutes les conditions de la règle n° 1 sont réunies, les symboles sont immédiatement mis à la poubelle. Plus précisément, leurs définitions sont effacées lorsque
Module
sort, mais les symboles eux-mêmes sont collectés dès qu'ils ne sont plus référencés par aucune expression. - b. Si 1.b et 1.c tiennent, mais que 1.a ne tient pas :
-
- Si les symboles ont
OwnValues
définis parSet
plutôt queSetDelayed
alors les symboles et leurs définitions survivent en dehors deModule
aussi longtemps qu'ils sont référencés dans le calcul qui utilise la valeur de retour deModule
- Si les symboles ont
-
- Si les symboles ont
OwnValues
définis parSetDelayed
, alors ils fuient vers la portée extérieure et y survivent indéfiniment, qu'ils soient / aient été référencés à l'extérieur ou non.
- Si les symboles ont
-
- c. Si 1.a et 1.b tiennent, mais que 1.c ne tient pas, les symboles et les définitions fuiront vers la portée extérieure et y survivront indéfiniment, qu'ils soient / aient été référencés à l'extérieur ou non.
- a. Si toutes les conditions de la règle n° 1 sont réunies, les symboles sont immédiatement mis à la poubelle. Plus précisément, leurs définitions sont effacées lorsque
-
Chaque fois que des symboles locaux sont référencés par des symboles externes, il se produit ce qui suit.
Module
sort :- a. Si le symbole local a
OwnValues
défini par des affectations immédiates (Set
, y compris dansModule
l'initialisation) et aucune autre*Values
défini et il ne contient pas d'autoréférences, alors ces symboles et leurOwnValues
ne sont conservés que tant que les symboles sont encore référencés de manière externe, et GC-d après cela. - b. Si un symbole local a soit
OwnValues
défini par des assignations retardées (SetDelayed
), soit des autoréférences, soit d'autres formes définies de*Values
(DownValues
,SubValues
,UpValues
), ces valeurs sont conservées / fuient dans la portée globale, indépendamment du fait que le symbole soit retourné à partir deModule
ou non.
- a. Si le symbole local a
-
Chaque fois que des symboles ont des références circulaires les uns aux autres, ils conservent leurs définitions (fuites, ne sont pas collectées/détruites) après le retour de
Module
sort, dans tous les cas, et qu'ils aient été référencés ou non par des symboles externes à l'intérieur deModule
.Module
. -
Le ramasseur d'ordures supprime tous les
Temporary
dès qu'ils satisfont à ces deux conditions :- N'ont pas de références (par d'autres symboles ou eux-mêmes).
- N'ont pas de définitions attachées (à l'exception des symboles avec des définitions existantes.
OwnValues
obtenus via des affectations immédiates /Set
tout en étant référencés par des symboles externes - dans ce cas, GC conservera à la fois le symbole et la définition jusqu'à ce que le symbole ne soit plus référencé, auquel cas il est collecté).
Exceptions et comportement déroutant
Il y a des cas où les règles ci-dessus ne tiennent pas, mais où le comportement de... Module
est suffisamment déroutant pour qu'il soit probablement plus logique de le catégoriser comme une exception plutôt que d'essayer de modifier les règles.
Comme illustré ci-dessous, notamment dans la section sur Module
et Unique
, unique Temporary
Les symboles fuient presque toujours lorsqu'ils ont des définitions retardées qui leur sont attachées, et c'est... Module
de les nettoyer dans les cas où il peut déterminer que la variable peut et doit être collectée. Le problème semble être que Module
ne fait pas vraiment un bon travail à ce sujet, dans tous les cas.
Variables locales dépendantes non cycliques avec définitions retardées.
Alors que la liste des exceptions va probablement s'allonger avec le temps, la première a été notée par Shadowray dans sa réponse, c'est l'exemple # 3 là.
DownValues
Fondamentalement, cette fuite de variable locale a
:
Module[{a, b},
a[y_] := 2 y;
b[y_] := 2 a[y];
b[1]
]
(* 4 *)
(les fuites peuvent être vues en utilisant la fonction vals
définie ci-dessous, de manière similaire aux autres exemples ci-dessous. Dans ce cas, il faudrait exécuter vals[DownValues]["a"]
), violant explicitement la règle #1 ci-dessus (puisque les 3 conditions sont réunies), alors que ceci ne le fait pas :
Module[{b, a},
a[y_] := 2 y;
b[y_] := 2 a[y];
b[1]
]
(* 4 *)
même si la seule différence est l'ordre des variables de l'expression Module
liste d'initialisation.
Le premier comportement ressemble à un Module
bug pour moi.
OwnValues
Une situation quelque peu similaire se produit pour OwnValues
. Le premier cas ici se présente comme suit :
Module[{a, b},
a := 2 ;
b := 2 a;
b
]
(* 4 *)
Dans ce cas, a
fait une fuite (évaluer vals[]["a"]
pour le voir, vals
défini ci-dessous), mais sa définition (OwnValues
) est effacée par Module
(contrairement au cas précédemment considéré de DownValues
). Pour l'autre :
Module[{b, a},
a := 2 ;
b := 2 a;
b
]
(* 4 *)
tout va bien comme avant.
Explication possible
Je ne peux que supposer que Module
, avant de sortir, "traite" les variables locales (dans le but d'effacer leurs définitions), dans le même ordre qu'elles apparaissent dans le fichier Module
liste d'initialisation. Par conséquent, dans le premier cas, a
est "traitée" en premier, et à ce moment-là, b
n'a pas encore été détruit, donc pour Module
on dirait que a
a un ref.count supplémentaire de b
et que, par conséquent, il n'efface pas a
et le fuit. Dans le second cas , b
est traité en premier et promptement détruit, puis a
est traité et également promptement détruit, puisqu'il n'a plus de référence de b
.
Statut de cette exception
Bien que j'aie catégorisé ce comportement comme une exception, il y a une explication plausible à cela. Donc, nous pouvons décider de promouvoir cela à une modification de la règle #1 à un moment donné, si d'autres preuves de son exactitude émergent.
Quelques implications
La principale implication de l'ensemble des règles ci-dessus est que le ramasseur de déchets n'est, dans la plupart des cas, pas assez intelligent pour collecter les symboles locaux temporaires, même lorsqu'ils ne sont plus référencés par aucun autre symbole, si ces symboles locaux ont quelques règles / définitions globales attachées.
Module
est responsable du nettoyage de ces définitions. Ainsi, à chaque fois que le symbole fuit en dehors de Module
avec des définitions qui lui sont attachées (sauf dans un cas spécifique de OwnValues
défini par Set
sans autoréférence, détaillé ci-dessous), il restera dans le système pendant un temps indéfini, même après qu'il ait cessé d'être référencé par tout autre symbole.
Illustration
Préparation
Nous supposerons pour tous les exemples ci-dessous qu'ils sont exécutés sur un noyau frais avec le code suivant exécuté en premier :
$HistoryLength = 0
vals[type_ : OwnValues][pattern_] :=
Map[
{#, ToExpression[#, StandardForm, type]} &,
Names["Global`" ~~ pattern ~~ "$*"]
]
Règle n°1
La règle #1 ne nécessite presque pas d'exemples particuliers, puisque c'est quelque chose que nous avons tous expérimenté de nombreuses fois. La condition 1.c peut nécessiter une illustration, que nous donnerons cependant avec les exemples de la règle n° 2 :
La règle n°2
2.a
Voici un exemple pour illustrer ce cas, que j'ai rendu un peu plus intéressant en faisant une référence au symbole lui-même :
Replace[
Module[{a}, a = Hold[a]; a],
Hold[s_] :> {s, OwnValues[s]}
]
vals[]["a"]
(* {a$713392, {}} *)
(* {} *)
ce que cela montre, c'est que si le symbole est bien renvoyé par Module
comme une partie de sa propre valeur dans Hold[a]
il n'a pas de OwnValues
en dehors de Module
- et est promptement collecté une fois Replace
se termine, comme le montre un appel à vals
.
2.b
Voici un exemple pour illustrer les cas 2.b.1 et 2.b.2
Replace[
Module[{a}, a = 1; Hold[a]],
Hold[sym_] :> OwnValues[sym]
]
vals[]["a"]
(* {HoldPattern[a$3063] :> 1} *)
(* {} *)
Cela montre que le symbole et sa définition survivent tous deux dans ce cas aussi longtemps qu'ils sont nécessaires dans le calcul englobant, et sont GC-d juste après.
Si nous changeons maintenant la façon dont nous avons défini les symboles locaux d'immédiat à différé, nous obtiendrons le cas couvert par 2.b.2 :
Replace[
Module[{a}, a := 1; Hold[a]],
Hold[sym_] :> OwnValues[sym]
]
vals[]["a"]
(* {HoldPattern[a$3060] :> 1} *)
(* {{"a$3060", {HoldPattern[a$3060] :> 1}}} *)
Un exemple observé par @Michael E2 relève également de la même catégorie :
ff[] := Module[{a}, a := 1; a /; True]
ff[]
Remove[ff]
vals[]["a"]
(* 1 *)
(* {{"a$3063", {HoldPattern[a$3063] :> 1}}} *)
Il n'est pas clair pour moi pourquoi les définitions retardées (devraient) empêcher le symbole d'être garbage - collected dans des cas comme celui-ci (voir aussi ci-dessous) et si c'est réellement un bug ou non.
2.c
Le cas 2.c a définitivement besoin d'une illustration :
Module[{a, b}, a = Hold[b]; b = Hold[a]; Length[{a, b}]]
(* 2 *)
vals[]["a" | "b"]
(*
{
{"a$3063", {HoldPattern[a$3063] :> Hold[b$3063]}},
{"b$3063", {HoldPattern[b$3063] :> Hold[a$3063]}}
}
*)
Cela peut en surprendre plus d'un, car les symboles ne sont pas renvoyés par la fonction Module
directement, ne sont pas référencés à l'extérieur et n'ont que des OwnValues
. Cependant, ils se référencent les uns les autres, et le GC de WL / Module
ne sont pas assez intelligents pour reconnaître qu'ils sont inaccessibles.
La règle n°3
C'est probablement la plus intéressante.
3.1
Voici une illustration simple pour celle-ci, où le symbole local... a
reçoit une définition immédiate et est référencé par le symbole externe s
:
ClearAll[s];
Module[{a}, a = 1; s := a];
s
(* 1 *)
On constate que a
reçoit la GC-d juste après que nous Remove
s
, comme promis :
vals[]["a"]
Remove[s]
vals[]["a"]
(* {{"a$2628", {HoldPattern[a$2628] :> 1}}} *)
(* {} *)
3.b
C'est probablement celui qui aura le plus d'exemples. Nous commençons par modifier l'exemple précédent de quelques façons.
Tout d'abord, faisons en sorte que le symbole local se référence lui-même :
ClearAll[s];
Module[{a}, a = Hold[1, a]; s := a];
{s, Last[s]}
(* {Hold[1, a$3063], Hold[1, a$3063]} *)
Dans ce cas, la suppression de la référence externe (symbole s
) ne sert à rien, puisque GC n'est pas capable de reconnaître l'autoréférence :
vals[]["a"]
Remove[s]
vals[]["a"]
(* {{"a$3063", {HoldPattern[a$3063] :> Hold[1, a$3063]}}} *)
(* {{"a$3063", {HoldPattern[a$3063] :> Hold[1, a$3063]}}} *)
Notez b.t.w., que les autoréférences sont reconnues dans les cas sans références externes :
Module[{a}, a = Hold[a]; a]
vals[]["a"]
(* Hold[a$3090] *)
(* {} *)
A mon avis Module
est assez intelligent pour reconnaître les autoréférences (mais pas les références mutuelles, comme nous l'avons vu) tant qu'il n'y a pas de références externes à un symbole - et qu'il décide ensuite de détruire les définitions du symbole - ce qui décrémente automatiquement le compte de réf. et rend le compte total de réf. du symbole 1
juste avant de quitter Module
et 0
juste après avoir quitté Module
ce qui le rend récupérable par le GC.
Quand il y a des références externes, Module
conserve également les définitions du symbole - c'est-à-dire qu'il ne les détruit pas en sortant. Puis plus tard, même lorsque la référence externe est supprimée, nous avons à la fois le symbole et sa définition présents, et le compte de réf. est toujours 1, puisque tant que la définition est présente, le symbole se référence lui-même. Ce qui le fait ressembler au GC à un symbole non récupérable.
Pour illustrer le cas suivant, créons OwnValues
avec SetDelayed
:
ClearAll[s];
Module[{a}, a := 1; s := a];
s
(* 1 *)
vals[]["a"]
Remove[s]
vals[]["a"]
(* {{"a$3067", {HoldPattern[a$3067] :> 1}}} *)
(* {{"a$3067", {HoldPattern[a$3067] :> 1}}} *)
Il est moins clair pour moi, pourquoi dans ce cas, le GC ne reconnaît pas le symbole comme collectable même après que les références externes aient été supprimées. Cela pourrait être considéré comme un bug, ou il pourrait y avoir une raison plus profonde et une justification de ce comportement, que je ne vois tout simplement pas.
Enfin, le cas de l'existence d'autres *Values
a été noté avant, et je vais voler un exemple (légèrement simplifié) de là :
Module[{g},
Module[{f},
g[x_] := f[x];
f[1] = 1
];
g[1]
]
(* 1 *)
vals[DownValues]["f" | "g"]
(* {{"f$", {}}, {"f$3071", {HoldPattern[f$3071[1]] :> 1}}} *)
Cela montre que même si la variable locale g
a elle-même été supprimée (puisque, alors qu'elle avait... DownValues
définie, elle n'était pas elle-même référencée de manière externe), la variable locale interne f
a fui, car, au moment où la variable locale interne Module
sortait, elle était encore référencée par g
.
Dans ce cas particulier, une façon (plutôt moche) de le récupérer est la suivante :
Module[{g, inner},
inner = Module[{f},
g[x_] := f[x];
f[1] = 1;
f
];
# &[g[1], Clear[[email protected]]]
]
(* 1 *)
où nous avons retourné la variable locale f
elle-même à partir de la variable interne Module
et l'avons placée dans inner
variable locale de l'élément extérieur Module
- ce qui a permis d'effacer ses définitions après g[1]
a été calculée :
vals[DownValues]["f" | "g"]
(* {{"f$", {}}} *)
de sorte que f
n'avait pas de définition et était donc GC-d (voir règle 5). J'ai montré cette solution de contournement non pas pour suggérer d'utiliser de telles constructions en pratique, mais plutôt pour illustrer la mécanique.
Les règles #4 et #5
Elles ont déjà été illustrées par les exemples ci-dessus.
Observations et spéculations
Module
et Unique
Les choses peuvent en fait être plus simples qu'elles n'y paraissent. Nous savons que le Module
mécanisme de localisation est basé sur Unique
. Nous pouvons utiliser cette connaissance pour tester quelle part du comportement observé de Module
provient réellement de l'interaction entre Unique
et le ramasseur d'ordures. Cela peut nous permettre de démystifier le rôle de Module
ici.
Considérons quelques exemples avec Unique
, qui seraient parallèles aux cas que nous avons déjà examinés dans le contexte de l'article Module
.
Tout d'abord, créons un unique Temporary
et observons simplement qu'il est immédiatement collecté :
Unique[a, Temporary]
vals[]["a"]
(* a$3085 *)
(* {} *)
Ensuite, nous l'enregistrons dans une variable, lui attribuons une certaine valeur, puis Remove
cette variable :
b = Unique[a, Temporary]
vals[]["a"]
Evaluate[b] = 1
vals[]["a"]
Remove[b]
vals[]["a"]
(* a$3089 *)
(* {{"a$3089", {}}} *)
(* 1 *)
(* {{"a$3089", {HoldPattern[a$3089] :> 1}}} *)
(* {} *)
Ici, la variable b
joue un rôle de Module
d'environnement, ce qui empêche la variable locale d'être immédiatement collectée alors qu'elle se trouve à l'intérieur de Module
. Ce que l'on constate, c'est que dès que l'on Remove
b
(penser - sortir Module
), la variable est détruite. Notez que la définition que nous avons donnée utilisait Set
.
Nous répétons maintenant la même chose mais en remplaçant Set
par SetDelayed
. De nouveau, la variable b
émule la variable Module
environnement :
b = Unique[a, Temporary]
Evaluate[b] := 1
vals[]["a"]
Remove[b]
vals[]["a"]
(* a$714504 *)
(* {{"a$714504", {HoldPattern[a$714504] :> 1}}} *)
(* {{"a$714504", {HoldPattern[a$714504] :> 1}}} *)
ce que nous venons de reproduire était un comportement déroutant de Module
par rapport aux variables locales assignées avec SetDelayed
.
Passons à autre chose et considérons les autoréférences faites avec Set
:
b = Unique[a, Temporary]
Evaluate[b] = Hold[Evaluate[b]]
vals[]["a"]
Remove[b]
vals[]["a"]
(* a$3070 *)
(* Hold[a$3070] *)
(* {{"a$3070", {HoldPattern[a$3070] :> Hold[a$3070]}}} *)
(* {{"a$3070", {HoldPattern[a$3070] :> Hold[a$3070]}}} *)
Nous avons à nouveau reproduit exactement le comportement que nous avons précédemment observé pour Module
.
Enfin, considérons le cas des références mutuelles :
c = Unique[a, Temporary]
d = Unique[b, Temporary]
With[{a = c, b = d},
a = Hold[b];
b = Hold[a];
]
vals[]["a" | "b"]
Remove[c, d]
vals[]["a" | "b"]
(* a$3070 *)
(* b$3071 *)
(*
{
{"a$3070", {HoldPattern[a$3070] :> Hold[b$3071]}},
{"b$3071", {HoldPattern[b$3071] :> Hold[a$3070]}}
}
*)
(*
{
{"a$3070", {HoldPattern[a$3070] :> Hold[b$3071]}},
{"b$3071", {HoldPattern[b$3071] :> Hold[a$3070]}}
}
*)
Où là encore, nous avons reproduit exactement le comportement que nous avons précédemment observé pour Module
.
Ce que nous pouvons conclure de ceci, c'est qu'une grande partie des comportements observés est en fait due au comportement sous-jacent de Unique
plutôt qu'à Module
.
Simple Module
émulation
Pour pousser les arguments précédents un peu plus loin encore, considérons l'émulation grossière suivante de Module
basée sur Unique
:
SetAttributes[myModule, HoldAll]
myModule[vars : {___Symbol}, body_] :=
Block[vars,
ReleaseHold[
Hold[body] /. Thread[vars -> Map[Unique[#, Temporary]&, vars]]
]
]
Cette émulation n'autorise pas l'initialisation dans la liste des variables, et remplace simplement toutes les occurrences de l'un des éléments suivants vars
dans le corps avec des symboles générés Temporary
symboles uniques, et laisse ensuite le corps s'évaluer.
Si vous réexécutez tous les exemples impliquant Module
avec myModule
vous observerez exactement les mêmes résultats dans tous les cas, sauf deux : l'exemple en 2.a et le dernier en 3.c. Mais ces comportements de l'originale Module
sont les moins déroutants, et les plus déroutants sont correctement reproduits avec la fonction myModule
.
Ainsi, alors que de toute évidence Module
fait plus que myModule
il se peut qu'il n'en fasse pas beaucoup plus. Cela déplace le problème sur l'interaction entre Unique
et le garbage collector, ce qui pourrait être considéré comme au moins une certaine réduction de la complexité.
Conclusions
Il semble que le comportement ou Module
en termes de fuite de symboles peut en général être décrit par un ensemble de règles raisonnablement simples. Des exceptions existent, mais là il semble qu'au moins elles puissent aussi avoir des explications plausibles.
Nous pouvons tirer plusieurs conclusions générales pour résumer le comportement décrit ci-dessus.
- Pour le garbage collection / symbol leaking, cela fait une différence si le symbole avait des références externes ou non, au moment où l'exécution quitte...
Module
- Le ramasseur d'ordures n'est pas assez intelligent pour recompter les autoréférences ou les références mutuelles formant des boucles fermées, après que l'exécution ait quitté...
Module
, et réaliser que certaines de ces variables locales sont devenues collectables. - En l'absence de références externes et d'autoréférences au moment où l'exécution du code quitte le
Module
,OwnValues
sont généralement bien en termes de collecte de symboles / pas de fuite. - Les symboles avec
OwnValues
créés par affectation immédiate (Set
) et sans autoréférence ne conservent leurs définitions que jusqu'à ce qu'ils soient référencés de manière externe (par d'autres symboles ou des expressions englobantes, s'ils sont renvoyés par la fonctionModule
), et sont promptement détruits / mis à la poubelle par la suite. - Symboles avec
OwnValues
conservent leurs définitions et ne sont donc pas collectés, dans les cas où on leur donne des définitions retardées (en utilisant le symboleSetDelayed
) et ils ont (encore) été référencés de manière externe au moment où l'exécution a quitté.Module
. Il n'est pas clair pourquoi il en est ainsi, et si oui ou non cela peut être considéré comme un bug. - Les symboles locaux avec
DownValues
et d'autres*Values
saufOwnValues
en général, fuiront / ne seront pas collectés si ils ont été référencés de manière externe au moment où l'exécution a quitté leurModule
, qu'ils soient ou non encore référencés extérieurement. - Dès qu'une
Temporary
a été supprimé, le symbole sera collecté tant qu'il n'est pas référencé à l'extérieur.
La plupart des comportements déroutants issus des observations ci-dessus peuvent être reproduits dans un cadre plus simple avec la fonction Module
émulé d'une manière très simple en utilisant Unique
variables. Il semble que cela ait plus à voir avec la dynamique de Unique
et le garbage collection, que Module
en soi. Il se peut que Module
ne fasse pas grand-chose de plus, à cet égard.
Je crois que la description ci-dessus est exacte et couvre tous les cas dont j'ai connaissance. Mais je peux facilement imaginer qu'il existe des cas que je n'ai pas vus ou pris en compte, qui rendraient le tableau plus complexe (ou peut-être, plus simple). Si vous connaissez de tels cas, ou d'autres mal décrits par ce schéma, merci de commenter.
Voici quelques exemples de fuites de mémoire inattendues dans Mathematica et comment les éviter :
1. Les fonctions de calcul en parallèle peuvent empêcher la collecte des ordures.
Module[{a}, Length[ParallelTable[a, {10}]]];
Names["a*"]
{"a", "a$1698"}
De même, lorsqu'un symbole temporaire est envoyé à un noyau parallèle, le Temporary
est effacé :
Module[{a}, ParallelTable[Attributes[a], {10}] ]
{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}}
Comment éviter ces fuites : N'envoyez pas de symboles temporaires vers ou depuis les noyaux parallèles.
2. La fonction de suivi de pile de Mathematica (introduite dans la v11) empêche le garbage collection si votre code produit des messages.
Module[{a}, a; 0/0];
Names["a*"]
{"a", "a$1697"}
Remarque : il n'y aura pas de fuite si vous définissez le paramètre $HistoryLength = 0
Comment éviter cette fuite : mettez $HistoryLength = 0
ou désactivez le menu des messages via
Internal`$MessageMenu = False
Voir aussi :
Comment désactiver la fonction de suivi de pile dans Mathematica 11 ?
3. Fonctions locales à l'intérieur Module
peuvent provoquer une fuite de mémoire si une fonction dépend d'une autre.
f[] := Module[{a, b},
a[y_] := 2 y;
b[y_] := 2 a[y];
b[1]
];
f[];
Names["a*"]
{"a", "a$1698"}
Notez que cette fuite ne nécessite ni autoréférences ni références circulaires comme dans la réponse de Leonid.
Il est remarquable que cette fuite disparaisse si on intervertit l'ordre des symboles dans le premier argument de Module
c'est-à-dire que le code suivant ne fuit pas :
f[] := Module[{b, a},
a[y_] := 2 y;
b[y_] := 2 a[y];
b[1]
];
f[];
Names["a*"]
{"a"}
Comment éviter cette fuite : éviter complètement les fonctions locales ou les supprimer explicitement avant de quitter le module, par ex :
f[] := Module[{a, b, result},
a[y_] := 2 y;
b[y_] := 2 a[y];
result = b[1];
Remove[a, b];
result
];
f[];
Names["a*"]
{"a"}
4. Fonctions locales à l'intérieur Module
provoquent une fuite de mémoire lorsqu'il y a un Condition
à l'intérieur de Module
f[x_] := Module[{a}, (a[y_] := y; a[x]) /; (x > 0)];
f[1];
Names["a*"]
{"a", "a$", "a$1698"}
Comment éviter cette fuite : Supprimez les fonctions locales de manière explicite, par ex.
f[x_] := Module[{a, result}, (a[y_] := y; result = a[x]; Remove[a]; result) /; (x > 0)];
f[1];
Names["a*"]
{"a", "a$"}
Enfin, pour ceux qui veulent aller plus loin dans le débogage du garbage collector de Mathematica, il existe une fonction, qui donne un nombre de références à un symbole donné :
System`Private`GetRefCount[f]
Si vous vous sentez motivé, vous avez la possibilité de laisser un article sur ce que vous avez pensé de cette déclaration.