elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Estamos en la red social de Mastodon


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  .NET (C#, VB.NET, ASP) (Moderador: kub0x)
| | | |-+  (SOLUCIONADO) Fuga de memória en una función :(
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: (SOLUCIONADO) Fuga de memória en una función :(  (Leído 5,910 veces)
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
(SOLUCIONADO) Fuga de memória en una función :(
« en: 4 Agosto 2013, 15:01 pm »

Tengo una fuga de memória grave en una aplicación, y es debido a una Class de terceros que estoy usando...

Este simple código aloja entre 100-200kb de RAM cada segundo :(

Código
  1. Public Class Form1
  2.  
  3.    Dim isbinded As Boolean = False
  4.    Dim Winamp As clsWACC = New clsWACC
  5.    Dim WithEvents mytimer As New Timer With {.Interval = 50, .Enabled = True}
  6.  
  7.    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles mytimer.Tick
  8.        isbinded = Winamp.Bind()
  9.        ' Label1.Text = isbinded
  10.        ' isbinded = Nothing
  11.    End Sub
  12.  
  13. End Class


Necesito usar un Timer rápido de menos de 50 ms, pero simplemente por curiosidad si aumento el Timer a 1000 ms la ram sigue subiendo sin pausa, aunque sube sólo 8 kb cada segundo.

He intentado examinar y corregir la Class pero no encuentro el fallo, esta es la parte de la Class que manipula el code de arriba:

Código
  1.        Public Sub New(ByVal handle As IntPtr, ByVal str As String)
  2.            'opens the process
  3.            hWinamp = OpenProcess(DAccess.PROCESS_ALL_ACCESS, False, waPID)
  4.            'If hWinamp.Equals(IntPtr.Zero) Then
  5.            '    'exception
  6.            'End If
  7.  
  8.            remStr = str
  9.            remoteBuf = AllocWinamp(handle, Convert.ToUInt32(str.Length + 1))
  10.            Dim localBuf As IntPtr = Marshal.StringToHGlobalAnsi(str)
  11.  
  12.            WriteProcessMemory(hWinamp, remoteBuf, localBuf, str.Length + 1, Nothing)
  13.            Marshal.FreeHGlobal(localBuf)
  14.        End Sub

Código
  1.    Dim path As String = Nothing
  2.    Dim regKey As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Winamp", False)
  3.  
  4.   'Binds to WinAmp
  5.    Public Function Bind() As Boolean
  6.        Return Bind("")
  7.    End Function
  8.  
  9.    Public Function Bind(ByVal PathToExecutable As String) As Boolean
  10.        hWnd_Winamp = WinAPI.FindWindow(lpClassName, Nothing)
  11.  
  12.        'waProcess = New Process
  13.        waProcess.EnableRaisingEvents = True
  14.  
  15.        'If WinAmp window handle not found, try to launch it
  16.        If hWnd_Winamp.Equals(IntPtr.Zero) Then
  17.  
  18.            'if path was not specified, try to find it in the Windows registry
  19.            If PathToExecutable = "" Then
  20.                'Dim path As String
  21.                'regKey = Registry.CurrentUser
  22.                'regKey = regKey.OpenSubKey("Software\Winamp", False)
  23.                If regKey IsNot Nothing Then
  24.                    path = Convert.ToString(regKey.GetValue(""))
  25.                    waProcess.StartInfo.FileName = path & "\Winamp.exe"
  26.                    regKey.Close()
  27.                Else
  28.                    Return False
  29.                End If
  30.            Else
  31.                waProcess.StartInfo.FileName = PathToExecutable
  32.            End If
  33.  
  34.            Try
  35.                waProcess.Start()
  36.            Catch ex As System.ComponentModel.Win32Exception When ex.ErrorCode = -2147467259
  37.                Debug.WriteLine("Executable not found")
  38.                Return False
  39.            Catch ex As Exception
  40.                Debug.WriteLine("unknown exception")
  41.                Return False
  42.            End Try
  43.  
  44.            waProcess.WaitForInputIdle()
  45.            hWnd_Winamp = waProcess.MainWindowHandle()
  46.            waPID = waProcess.Id
  47.        Else
  48.  
  49.            'WinAmp handle found
  50.            'now bind to WinAmp process
  51.            'get PID from hWnd
  52.  
  53.            WinAPI.GetWindowThreadProcessId(hWnd_Winamp, waPID)
  54.            If waPID = 0 Then
  55.                Return False
  56.            End If
  57.  
  58.            waProcess = Process.GetProcessById(waPID)
  59.            waProcess.EnableRaisingEvents = True
  60.  
  61.            hWnd_Playlist = GetHWND(WinampWindow.Playlist)
  62.            hWnd_Equalizer = GetHWND(WinampWindow.Equalizer)
  63.            hWnd_Video = GetHWND(WinampWindow.Video)
  64.            Return True
  65.  
  66.        End If
  67.  
  68.        'if hWnd of the main window is still zero,
  69.        'it was not possible to launch or bind to WinAmp
  70.        If hWnd_Winamp.Equals(IntPtr.Zero) Then
  71.            Return False
  72.        Else
  73.            hWnd_Playlist = GetHWND(WinampWindow.Playlist)
  74.            hWnd_Equalizer = GetHWND(WinampWindow.Equalizer)
  75.            hWnd_Video = GetHWND(WinampWindow.Video)
  76.            Return True
  77.        End If
  78.    End Function

Pero la primera condición no se cumple, es decir, solo se manipula esta parte de la función Bind:

Código
  1.        If hWnd_Winamp.Equals(IntPtr.Zero) Then
  2.             ' NADA
  3.        Else
  4.  
  5.            'WinAmp handle found
  6.            'now bind to WinAmp process
  7.            'get PID from hWnd
  8.  
  9.            WinAPI.GetWindowThreadProcessId(hWnd_Winamp, waPID)
  10.            If waPID = 0 Then
  11.                Return False
  12.            End If
  13.  
  14.            waProcess = Process.GetProcessById(waPID)
  15.            waProcess.EnableRaisingEvents = True
  16.  
  17.            hWnd_Playlist = GetHWND(WinampWindow.Playlist)
  18.            hWnd_Equalizer = GetHWND(WinampWindow.Equalizer)
  19.            hWnd_Video = GetHWND(WinampWindow.Video)
  20.            Return True
  21.  
  22.        End If

hWnd_Winamp es un IntPtr
waProcess es un Process (el cual he probado a liberarlo pero sigue pasando lo mismo...)
waPID es un Integer
hWnd_Playlist es un IntPtr
hWnd_Equalizer es un IntPtr
hWnd_Video es un IntPtr

...y GetWindowThreadProcessId es esta función:

Código
  1.        '========================                        =========================
  2.        '======================  GetWindowThreadProcessId  =======================
  3.        '========================                        =========================
  4.        'Retrieves the identifier of the thread that created the specified window
  5.        'and, optionally, the identifier of the process that created the window.
  6.        <DllImport("user32.dll", SetLastError:=True)> _
  7.        Public Shared Function GetWindowThreadProcessId( _
  8.                                        ByVal hwnd As IntPtr, _
  9.                                        ByRef lpdwProcessId As Integer) As Int32
  10.        End Function


y esta la función GetHWND:
Código
  1.    Private Shared Function GetHWND(ByVal Window As WinampWindow) As IntPtr
  2.        Return SendWA_IPC(Window, Message.IPC_GETWND)
  3.    End Function
  4.  
  5.    Private Shared Function SendWA_IPC(ByVal param As Int64, ByVal MessageName As Message) As IntPtr
  6.        Return WinAPI.SendMessage(hWnd_Winamp, WinAPI.Msg.WM_USER, IntPtr.op_Explicit(param), IntPtr.op_Explicit(MessageName))
  7.    End Function
  8.  
  9.        '===============================           ===============================
  10.        '=============================  SendMessage  =============================
  11.        '===============================           ===============================
  12.        'Sends the specified message to a window or windows. It calls the window
  13.        'procedure for the specified window and does not return until the window
  14.        'procedure has processed the message.
  15.        <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
  16.        Public Shared Function SendMessage(ByVal hWnd As IntPtr, _
  17.                                           ByVal Msg As Msg, _
  18.                                           ByVal wParam As IntPtr, _
  19.                                           ByVal lParam As IntPtr) As IntPtr
  20.        End Function


¿Alguna sugerencia?

Gracias...


« Última modificación: 8 Agosto 2013, 18:13 pm por EleKtro H@cker » En línea

Novlucker
Ninja y
Colaborador
***
Desconectado Desconectado

Mensajes: 10.683

Yo que tu lo pienso dos veces


Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #1 en: 7 Agosto 2013, 17:17 pm »

Hay algo que no entiendo y es ... ¿Por que ese necesario buscar cada 50 ms la ventana y el proceso de Winamp? Está claro que es algo muy costoso, ¿no alcanza con obtenerlo una vez?

Saludos


En línea

Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD
"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #2 en: 7 Agosto 2013, 17:29 pm »

Hay algo que no entiendo y es ... ¿Por que ese necesario buscar cada 50 ms la ventana y el proceso de Winamp? Está claro que es algo muy costoso

La intención de repetirlo cada 50 ms es para detectar cuando el Winamp está en modo "Pausa" o en "Detenido", o para detectar si el proceso sigue activo o si ya no se está ejecutando el Winamp, si se te ocurre una forma más eficiente plz dímelo.

EDITO:


Para que entiendas lo que hago, tengo algo así:

Código
  1. Dim WinampIsRunning = winamp.bind() ' Esto devuelve True si el proceso de Winamp se está ejecutando
  2.  
  3. If WinampIsRunning then
  4.  
  5.    if winamp.state = winamp.state.Paused then...
  6.    ' actualizar posición del trackbar, labels, textboxes, etc...
  7.  
  8.    elseif winamp.state = winamp.state.Paused then...
  9.  
  10.    elseif winamp.state = winamp.state.Stoped then...
  11.  
  12. end if

PD: ¿Entonces tu tampoco encuentras el problema en el code de arriba?

Saludos
« Última modificación: 7 Agosto 2013, 17:35 pm por EleKtro H@cker » En línea

Novlucker
Ninja y
Colaborador
***
Desconectado Desconectado

Mensajes: 10.683

Yo que tu lo pienso dos veces


Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #3 en: 7 Agosto 2013, 17:55 pm »

¿No hay un evento en esa clase para detectar el cambio de estado?

En cuanto al proceso por ejemplo. Lo obtienes todas las veces y seteas EnableRaisingEvents en true, lo que hay que hacer es obtener el proceso, setear EnableRaisingEvents en true, suscribirse al evento Exited, y dejar de buscar el proceso. Cuando se dispara el evento Exited, entonces volvemos a buscar.

Saludos
En línea

Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD
"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #4 en: 7 Agosto 2013, 18:08 pm »

¿Por que ese necesario buscar cada 50 ms la ventana y el proceso de Winamp? Está claro que es algo muy costoso

Aún así ese trozo de código de la Class genera memoria sin cesar, aunque se use un Timer que tickee cada 10 segundos que llame a la función Bind, la cantidad de memoria en ese caso será minima pero al fin y al cabo incesante, hay algo que está mal en ese code.

¿No hay un evento en esa clase para detectar el cambio de estado?

En cuanto al proceso por ejemplo. Lo obtienes todas las veces y seteas EnableRaisingEvents en true, lo que hay que hacer es obtener el proceso, setear EnableRaisingEvents en true, suscribirse al evento Exited, y dejar de buscar el proceso. Cuando se dispara el evento Exited, entonces volvemos a buscar.

la Class del Winamp son 2000 o 3000 lineas de código, no me lo he mirado todo, eso es lo próximo que haré y ya te cuento.
En línea

Novlucker
Ninja y
Colaborador
***
Desconectado Desconectado

Mensajes: 10.683

Yo que tu lo pienso dos veces


Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #5 en: 7 Agosto 2013, 19:39 pm »

Pero en principio te puedes fijar si tiene un evento, solo es poner punto en el nombre de instancia de clase y buscar el que tiene un "rayito" :xD

Saludos
En línea

Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD
"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #6 en: 7 Agosto 2013, 19:50 pm »

Pero en principio te puedes fijar si tiene un evento, solo es poner punto en el nombre de instancia de clase y buscar el que tiene un "rayito" :xD
Si pero si el evento fuese privado por cualquier motivo pues no me iba a salir en el intellisense, es que en la class hay muchos "Tests" como cosas de prueba y cosas por mejorar, no está del todo perfeccionado así que tenía que usar el buscador de la IDE para examinar la class a fondo xD.


Citar
¿No hay un evento en esa clase para detectar el cambio de estado?
No, ninguno, hay que crear el evento o detectar el estado a lo cutre, mediante condiciones (if's).


En fín Novlucker he seguido tus indicaciones para lo del proceso:

En la class del winamp:
Código
  1.    Private Sub waProcess_Exited(ByVal sender As Object, ByVal e As EventArgs) Handles waProcess.Exited
  2.        RaiseEvent WinampExited()
  3.    End Sub

En la class de mi form:
Código
  1.    Private Sub Exited() Handles Winamp.WinampExited
  2.        Winamp_IsRunning = False
  3.    End Sub
  4.  
  5.    Private Sub Monitor_Timer_Tick(sender As Object, e As EventArgs) Handles Monitor_Timer.Tick
  6.        If Not Winamp_IsRunning Then Winamp_IsRunning = Winamp.Bind()
  7.    End sub
  8.  

Se nota mucho el cambio, gracias, aunque como ya digo la función Bind de esa class tiene algún problema así que por mucho que yo intente perfeccionar estas minucias no va a dejar de subir la RAM xD, aunque ahora he conseguido que suba reálmente muy poco a poco, el consumo de ram hace subidas y bajadas que no me gustan nada, pero bueno parece estar equilibrado, sube un poco y al rato como que el GC hace su trabajo, lo libera y vuelve a bajar el consumo, y vuelve a subir, en fin xD. (Todo esto dejando el proceso en "StandBy")

Saludos
« Última modificación: 7 Agosto 2013, 20:00 pm por EleKtro H@cker » En línea

Novlucker
Ninja y
Colaborador
***
Desconectado Desconectado

Mensajes: 10.683

Yo que tu lo pienso dos veces


Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #7 en: 7 Agosto 2013, 20:31 pm »

Yo creo que el problema está no en la función, sino en ejecutar esa función constantemente tan rápido. Se crean más datos de los que se alcanzan a liberar.

Te pongo un ejemplo,
Código
  1. using System;
  2. using System.Diagnostics;
  3. using System.Timers;
  4.  
  5. namespace ConsoleApplication6
  6. {
  7.    class Program
  8.    {
  9.        static void Main(string[] args)
  10.        {
  11.            Timer timer = new Timer(50); //Para una aplicacion de consola toca usar este Timer
  12.            //http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
  13.            timer.Elapsed += new ElapsedEventHandler(Run);
  14.            timer.Start();
  15.            Console.ReadLine();
  16.        }
  17.  
  18.        static void Run(object sender, EventArgs e)
  19.        {
  20.            Console.WriteLine(DateTime.Now);
  21.            for (int i = 0; i < 2000; i++)
  22.            {
  23.                Process.GetProcesses();
  24.            }
  25.        }
  26.    }
  27. }

El código es muy simple, pero si lo ejecutas verás como la RAM va aumentando progresivamente :P

Saludos
En línea

Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD
"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #8 en: 7 Agosto 2013, 20:53 pm »

Yo creo que el problema está no en la función, sino en ejecutar esa función constantemente tan rápido. Se crean más datos de los que se alcanzan a liberar.

Lo que dices es muy lógico, es lo primero que se llega a pensar, puede que si que quizás en 50 ms se generen más datos de los que se pueden liberar, pero además de eso estoy seguro de que hay una fuga en esa función, y la prueba definitiva la di al principio, con este code:


Código
  1.    Public Class Form1
  2.  
  3.       Dim isbinded As Boolean = False
  4.       Dim Winamp As clsWACC = New clsWACC
  5.       Dim WithEvents mytimer As New Timer With {.Interval = 50, .Enabled = True}
  6.  
  7.       Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles mytimer.Tick
  8.           isbinded = Winamp.Bind()
  9.           ' Label1.Text = isbinded
  10.           ' isbinded = Nothing
  11.       End Sub
  12.  
  13.    End Class

Símplemente el Test consiste en crear una APP con ese código (y adjuntar la dll de la función Bind claro xD).

Repito que si uso un Timer de 10 segundos (o 20, o los que sean) la RAM sigue subiendo progresívamente, solo que se generarán los bytes mucho más lento porque el timer es más lento así que hay que dejar la app un buen rato corriendo para apreciar el aumentado del consumo de RAM, pero en fín yo creo que en 10-20 segundos el GC tiene tiempo de liberar...

Saludos!
« Última modificación: 7 Agosto 2013, 20:54 pm por EleKtro H@cker » En línea

Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.818



Ver Perfil
Re: Fuga de memória en una función :(
« Respuesta #9 en: 8 Agosto 2013, 18:13 pm »

Bueno creo que puedo dar este tema por solucionado...
me ha costado mucho tiempo corregir cada parte del código de la aplicación que tenían fugas de memória pero al final he estabilizado el consumo y ya no aumenta progresívamente, de los 12 MB no pasa nunca el consumo ahora,
ya puedo dejar de colectar manuálmente con el GC y de usar API's para flushear el consumo de memória de la APP xD

Gracias por tu tiempo Novlucker.


Lo único que me gustaría mejorar sobre el tema de la memória es lo que comento aquí:     Tema: Experimento de consumo de memória... ver para creer!  (Leído 58 veces)


Saludos!
En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Punto de fuga
Diseño Gráfico
pumass 0 1,595 Último mensaje 1 Agosto 2006, 23:56 pm
por pumass
Ayuda! Por favor. Tengo problema con memoria dinámica en c++ y función getline
Programación C/C++
DeathStar92 0 2,087 Último mensaje 25 Noviembre 2012, 19:56 pm
por DeathStar92
[JS] Encontrar la causa de la fuga de memoria en este Gadget
Scripting
Eleкtro 3 2,591 Último mensaje 24 Diciembre 2014, 09:08 am
por Eleкtro
Qué es una fuga de memoria RAM y cómo arreglarla
Noticias
wolfbcn 0 1,268 Último mensaje 30 Enero 2017, 21:32 pm
por wolfbcn
Llamada a una funcion en un esquema de memoria segmentada « 1 2 »
Programación C/C++
Usuario887 13 6,806 Último mensaje 25 Abril 2020, 18:13 pm
por Usuario887
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines