Skip to content

Qu'est-ce qui provoque la fuite des variables du module ?

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) :

  1. 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.
  2. 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 :
        1. Si les symboles ont OwnValues définis par Set plutôt que SetDelayedalors les symboles et leurs définitions survivent en dehors de Module aussi longtemps qu'ils sont référencés dans le calcul qui utilise la valeur de retour de Module
        1. Si les symboles ont OwnValues définis par SetDelayed, 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.
    • 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.
  3. 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 dans Module l'initialisation) et aucune autre *Values défini et il ne contient pas d'autoréférences, alors ces symboles et leur OwnValues 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 de Module ou non.
  4. 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 de Module . Module.

  5. 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 Moduleet 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 bet 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 Moduleavec 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 Removes, 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 Moduleet 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 Modulesortait, 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 Moduled'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 Removeb (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 Uniqueplutô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 myModulevous 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 myModuleil 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 Moduleen 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 fonction Module), 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 symbole SetDelayed) 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 sauf OwnValuesen 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é leur Module , 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 Moduleprovoquent 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.


Tags : /

Utilisez notre moteur de recherche

Ricerca
Generic filters

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.