[.NET] Libérer de la mémoire sur des objets volumineux

Libérer de la mémoire sur des objets volumineux [.NET] - C#/.NET managed - Programmation

Marsh Posté le 23-04-2007 à 16:51:01    

Bonjour,
 
Je suis en train de faire une reprise de données sur un catalogue et je dois notament passer d'un champ texte "image" à 2 champ dans la nouvelle base de données (SqlServer) de type image : un pour l'image, l'autre pour la miniature. Pour celà je dois donc ouvrir l'image a partir de son lien et ensuite l'enregistrer dans ma base de données.
 
Je fais déjà monImage.dispose(), maMiniature.dispose() et mon objet monProduit.dispose() qui contient toutes les propriétés et qui fait l'insert dans la base de données, mais malgrès ça, lorsque j'exécute et que je regarde les processus, la mémoire ne cesse d'augmenter jusqu'à saturation et message d'erreur "Mémoire insufisante".
 

Code :
  1. Public Function AjoutImage(ByVal oProduit As Dal.ClsTpProduit, ByVal oNomFichier As String, ByVal oCount As Int32) As Boolean
  2.         Dim oCheminFichier As String
  3.         Dim oFileInfo As IO.FileInfo
  4.         Dim oProduitImage As New Dal.ClsTpProduitImage(oNomFichier)
  5.         Dim oImage As Image
  6.         Dim oResult As Boolean
  7.         Dim oProduitColoris As Dal.ClsTpProduitColoris
  8.         'Mois et annee pour le dossier image'
  9.         Dim oMois As String = oProduit.CreeLe.Month.ToString("00" )
  10.         Dim oAnnee As String = oProduit.CreeLe.Year.ToString("0000" )
  11.         'Recherche de l image'
  12.         oCheminFichier = "Z:\ " & oAnnee & "\ " & oMois & "-" & oAnnee.Substring(2, 2) & "\ " & oNomFichier.Replace("jpg", "JPG" )
  13.         oFileInfo = New IO.FileInfo(oCheminFichier)
  14.         Try
  15.             'Propriétés de connection'
  16.             oProduitImage.Connection = oProduit.Connection
  17.             oProduitImage.Transaction = oProduit.Transaction
  18.             'Propriétés de lobjet image'
  19.             oProduitImage.ProduitUid = oProduit.ProduitUid
  20.             'Dates et modifieur'
  21.             oProduitImage.ModifieLe = oProduit.ModifieLe
  22.             oProduitImage.ModifieParUid = oProduit.ModifieParUid
  23.             oProduitImage.CreeLe = oProduit.CreeLe
  24.             oProduitImage.CreeParUid = oProduit.CreeParUid
  25.             If IO.File.Exists(oCheminFichier) Then
  26.                 Try       
  27.                     oProduitImage.Nom = oFileInfo.Name.Split("."c)(0)
  28.                     oProduitImage.Extension = oFileInfo.Extension.Replace(".", "" )
  29.                     oProduitImage.Numero = oCount
  30.                     'Création de l image et de la miniature'
  31.                     oProduitImage.Document = xCasaque.xIOCommon.GetByteByFile(oFileInfo.FullName)
  32.                     oImage = xCasaque.xImage.GetImageByByte(oProduitImage.Document, New Size(250, 250))
  33.                     oProduitImage.Miniature = xCasaque.xImage.GetByteByImage(oImage, oProduitImage.Extension)
  34.                     'Enregistrement de l image'
  35.                     oResult = oProduitImage.Save
  36.                     'Libération de l image'
  37.                     oImage.Dispose()
  38.                 Catch ex As Exception
  39.                     oResult = False
  40.                     Throw ex
  41.                 End Try
  42.             If Not oResult Then
  43.                 Exit Try
  44.             End If
  45.             'Enregistrement du Produitcoloris'
  46.             oProduitColoris = New Dal.ClsTpProduitColoris(oProduit.ProduitUid, Bll.MesValeursDefaut.CouleurNonSpecifieeUid)
  47.             'Propriétés de connection'
  48.             oProduitColoris.Connection = oProduit.Connection
  49.             oProduitColoris.Transaction = oProduit.Transaction
  50.             'Affectation des paramètres'
  51.             oProduitColoris.ProduitImageUid = oProduitImage.ProduitImageUid
  52.             oProduitColoris.Reference = oProduit.Reference
  53.             oProduitColoris.Motif = False
  54.             'Dates et modifieur'
  55.             oProduitColoris.ModifieLe = oProduit.ModifieLe
  56.             oProduitColoris.ModifieParUid = oProduit.ModifieParUid
  57.             oProduitColoris.CreeLe = oProduit.CreeLe
  58.             oProduitColoris.CreeParUid = oProduit.CreeParUid
  59.             'Enregistrement du produit coloris'
  60.             oResult = oProduitColoris.Save()
  61.             oProduitImage.Dispose()
  62.         Catch ex As Exception
  63.             oResult = False
  64.             Throw ex
  65.         End Try
  66.         Return oResult
  67.     End Function


 
Est-ce que quelqu'un a une idée pour m'aider à résoudre ce problème ?
 
Merci d'avance :hello:


Message édité par naglafar le 23-04-2007 à 17:57:42
Reply

Marsh Posté le 23-04-2007 à 16:51:01   

Reply

Marsh Posté le 23-04-2007 à 21:12:59    

fait un collect afin de forcer le garbage collector de passer sur les objets

Reply

Marsh Posté le 24-04-2007 à 10:56:40    

Je me suis renseigné sur le GC.Collect, ça semble en effet être la bonne façon de faire pour libérer la mémoire, mais malheureusement ça ne change rien dans mon cas : dans mon exécution il passe dans le GC.Collect mais quand je regarde dans le gestionnaire des tâches, la mémoire ne cesse d'augmenter. Pourtant les objets volumineux appellent tous la fonction Dispose donc il devrait pouvoir me les vider :(

Reply

Marsh Posté le 24-04-2007 à 13:06:38    

Je parle pas très bien VB et j'ai pas trop de temps pour analyser ton code, mais pour les objets à libérer :
* de manière générale vaut mieux faire un Dispose() et les mettre à null juste après.
 
Sinon ceux qu'il faut flinguer prioritairement :
* les images (comme tu l'as fait)
* tous les outils de GDI+  (Pen, Brush, Font)
* la connection à ta base de donnée
* les sql commandes
* les transactions
 
En l'occurence, très rapidement, essaye d'abord de mettre à null les objets que tu Dispose(), et dis-nous si ça résoud un peut tes pb.

Reply

Marsh Posté le 25-04-2007 à 13:30:38    

moi23372 a écrit :

fait un collect afin de forcer le garbage collector de passer sur les objets


 
Très mauvaise idée. Le Collect effectue un parcours de la zone mémoire alloué à la CLR et la defrag en meme temps qu'elle libère des blocks de mémoire. C'est énormément chronophage (et niveau conso cpu, c'est pareil). D'une manière générale, il faut toujours laisser le système s'occuper de ce genre de chose, on est pas en C.
 
S'il a un memory leak, c'est qu'il y a un problème dans son code. Soigner le symptome n'est pas la solution. Il faut soigner la maladie.


---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 25-04-2007 à 13:32:31    

rapidement je peux deja dire que remplacer ses horribles concaténation de string par un stringbuilder lui améliorera la mémoire.
 
Edit : ce qui consomme le plus ce sont les "string" car c'est immutable et ce n'est jamais libéré par le garbage collector.

Message cité 1 fois
Message édité par Tamahome le 25-04-2007 à 13:33:31

---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 25-04-2007 à 14:18:04    

Tamahome a écrit :

rapidement je peux deja dire que remplacer ses horribles concaténation de string par un stringbuilder lui améliorera la mémoire.

Pas en .Net 2.0
Ca a été amélioré :)
 
Et sinon je suis à 100% d'accord avec toi sur le GC.Collect().

Reply

Marsh Posté le 25-04-2007 à 15:46:44    

Après plusieurs tests, j'i vu que GC.Collect() fonctionnait mais qu'il ne libérait que peu d'espace, il reste des objets très volumineux. J'ai changé le String en StringBuilder, ça a un peu diminué la mémoire mais pas encore assez. Ca atteint rapidement les 1 Go de mémoire utilisé donc ça ne peut pas être que sur des chaînes de caractères, il y a surement les images manipulées qui interviennent là dedans.
 
En fait je me demande si ça ne viendrait pas de ma table d'origine. Ma source c'est une DataTable assez grosse (8000 lignes) et qui contient aussi des images et je boucle avec un For oI = 0 To oDataTable.Count - 1 dedans. Seulement ce qui est étonnant c'est que je la charge intégralement dès le début de la migration et que ensuite je ne sais que l'exploiter, donc sa taille ne devrait pas augmenter.

Reply

Marsh Posté le 25-04-2007 à 17:17:27    

tu peux essayer  
- d'implémenter IDisposable dans tes objets et du coup de faire des using pour les objets créés dans cette méthode, et probablement dans la méthode qui appelle celle-ci sûrement en boucle,
- passer tes arguments par référence
 
 
edit : un très bon lien sur le GC (et les using, IDisposable, WeakReference..)
 
http://www.dotnetguru.org/articles/GC/GC.html


Message édité par pikti le 26-04-2007 à 09:48:44
Reply

Marsh Posté le 26-04-2007 à 07:28:12    

naglafar a écrit :

Après plusieurs tests, j'i vu que GC.Collect() fonctionnait mais qu'il ne libérait que peu d'espace, il reste des objets très volumineux. J'ai changé le String en StringBuilder, ça a un peu diminué la mémoire mais pas encore assez. Ca atteint rapidement les 1 Go de mémoire utilisé donc ça ne peut pas être que sur des chaînes de caractères, il y a surement les images manipulées qui interviennent là dedans.
 
En fait je me demande si ça ne viendrait pas de ma table d'origine. Ma source c'est une DataTable assez grosse (8000 lignes) et qui contient aussi des images et je boucle avec un For oI = 0 To oDataTable.Count - 1 dedans. Seulement ce qui est étonnant c'est que je la charge intégralement dès le début de la migration et que ensuite je ne sais que l'exploiter, donc sa taille ne devrait pas augmenter.


 
Essaye en ne chargeant que la ligne dont tu as besoin. Utilise des using{} a la place des dispose, c'est plus pratique (ca appelle automatiquement le dispose. Sinon faut mettre les dispose() dans un finaly dans tes blocs try/catch pour etre sur que ce soit appellé...).  
 
T'as posté l'intégralité du code au fait ? Si je le prends, ca compile ? (j'ai pas essayé)
 
Edit : ah merde c'est du vb.net [:skeye]


Message édité par Tamahome le 26-04-2007 à 07:28:40

---------------
Hobby eien /人◕ ‿‿ ◕人\
Reply

Marsh Posté le 26-04-2007 à 07:28:12   

Reply

Marsh Posté le 26-04-2007 à 16:16:00    

Merci beaucoup pour votre aide. J'ai trouvé d'où vient le problème mais vous ne pouviez pas le voir...
 
en fait c'est ma fonction qui génère la miniature de l'image qui ne libère pas bien la mémoire
 
oImage = xCasaque.xImage.GetImageByByte(oProduitImage.Document, New Size(250, 250))
oProduitImage.Miniature = xCasaque.xImage.GetByteByImage(oImage, oProduitImage.Extension)
 
J'ai pas encore cherché à quel endroit il y a un objet qui n'a pas été libéré mais maintenant que je sais d'où ça vient ça devrait être plus facile.

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed