Autor
|
Tema: (SOLUCIONADO) Fuga de memória en una función :( (Leído 6,344 veces)
|
Eleкtro
Ex-Staff
Desconectado
Mensajes: 9.866
|
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 Public Class Form1 Dim isbinded As Boolean = False Dim Winamp As clsWACC = New clsWACC Dim WithEvents mytimer As New Timer With {.Interval = 50, .Enabled = True} Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles mytimer.Tick isbinded = Winamp.Bind() ' Label1.Text = isbinded ' isbinded = Nothing End Sub 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: Public Sub New(ByVal handle As IntPtr, ByVal str As String) 'opens the process hWinamp = OpenProcess(DAccess.PROCESS_ALL_ACCESS, False, waPID) 'If hWinamp.Equals(IntPtr.Zero) Then ' 'exception 'End If remStr = str remoteBuf = AllocWinamp(handle, Convert.ToUInt32(str.Length + 1)) Dim localBuf As IntPtr = Marshal.StringToHGlobalAnsi(str) WriteProcessMemory(hWinamp, remoteBuf, localBuf, str.Length + 1, Nothing) Marshal.FreeHGlobal(localBuf) End Sub
Dim path As String = Nothing Dim regKey As RegistryKey = Registry.CurrentUser.OpenSubKey("Software\Winamp", False) 'Binds to WinAmp Public Function Bind() As Boolean Return Bind("") End Function Public Function Bind(ByVal PathToExecutable As String) As Boolean hWnd_Winamp = WinAPI.FindWindow(lpClassName, Nothing) 'waProcess = New Process waProcess.EnableRaisingEvents = True 'If WinAmp window handle not found, try to launch it If hWnd_Winamp.Equals(IntPtr.Zero) Then 'if path was not specified, try to find it in the Windows registry If PathToExecutable = "" Then 'Dim path As String 'regKey = Registry.CurrentUser 'regKey = regKey.OpenSubKey("Software\Winamp", False) If regKey IsNot Nothing Then path = Convert.ToString(regKey.GetValue("")) waProcess.StartInfo.FileName = path & "\Winamp.exe" regKey.Close() Else Return False End If Else waProcess.StartInfo.FileName = PathToExecutable End If Try waProcess.Start() Catch ex As System.ComponentModel.Win32Exception When ex.ErrorCode = -2147467259 Debug. WriteLine("Executable not found") Return False Catch ex As Exception Debug. WriteLine("unknown exception") Return False End Try waProcess.WaitForInputIdle() hWnd_Winamp = waProcess.MainWindowHandle() waPID = waProcess.Id Else 'WinAmp handle found 'now bind to WinAmp process 'get PID from hWnd WinAPI.GetWindowThreadProcessId(hWnd_Winamp, waPID) If waPID = 0 Then Return False End If waProcess = Process.GetProcessById(waPID) waProcess.EnableRaisingEvents = True hWnd_Playlist = GetHWND(WinampWindow.Playlist) hWnd_Equalizer = GetHWND(WinampWindow.Equalizer) hWnd_Video = GetHWND(WinampWindow.Video) Return True End If 'if hWnd of the main window is still zero, 'it was not possible to launch or bind to WinAmp If hWnd_Winamp.Equals(IntPtr.Zero) Then Return False Else hWnd_Playlist = GetHWND(WinampWindow.Playlist) hWnd_Equalizer = GetHWND(WinampWindow.Equalizer) hWnd_Video = GetHWND(WinampWindow.Video) Return True End If End Function
Pero la primera condición no se cumple, es decir, solo se manipula esta parte de la función Bind: If hWnd_Winamp.Equals(IntPtr.Zero) Then ' NADA Else 'WinAmp handle found 'now bind to WinAmp process 'get PID from hWnd WinAPI.GetWindowThreadProcessId(hWnd_Winamp, waPID) If waPID = 0 Then Return False End If waProcess = Process.GetProcessById(waPID) waProcess.EnableRaisingEvents = True hWnd_Playlist = GetHWND(WinampWindow.Playlist) hWnd_Equalizer = GetHWND(WinampWindow.Equalizer) hWnd_Video = GetHWND(WinampWindow.Video) Return True 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: '======================== ========================= '====================== GetWindowThreadProcessId ======================= '======================== ========================= 'Retrieves the identifier of the thread that created the specified window 'and, optionally, the identifier of the process that created the window. <DllImport("user32.dll", SetLastError:=True)> _ Public Shared Function GetWindowThreadProcessId( _ ByVal hwnd As IntPtr, _ ByRef lpdwProcessId As Integer) As Int32 End Function
y esta la función GetHWND: Private Shared Function GetHWND(ByVal Window As WinampWindow) As IntPtr Return SendWA_IPC(Window, Message.IPC_GETWND) End Function Private Shared Function SendWA_IPC(ByVal param As Int64, ByVal MessageName As Message) As IntPtr Return WinAPI.SendMessage(hWnd_Winamp, WinAPI.Msg.WM_USER, IntPtr.op_Explicit(param), IntPtr.op_Explicit(MessageName)) End Function '=============================== =============================== '============================= SendMessage ============================= '=============================== =============================== 'Sends the specified message to a window or windows. It calls the window 'procedure for the specified window and does not return until the window 'procedure has processed the message. <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ Public Shared Function SendMessage(ByVal hWnd As IntPtr, _ ByVal Msg As Msg, _ ByVal wParam As IntPtr, _ ByVal lParam As IntPtr) As IntPtr 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
Mensajes: 10.683
Yo que tu lo pienso dos veces
|
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
Mensajes: 9.866
|
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í: Dim WinampIsRunning = winamp.bind() ' Esto devuelve True si el proceso de Winamp se está ejecutando If WinampIsRunning then if winamp.state = winamp.state.Paused then... ' actualizar posición del trackbar, labels, textboxes, etc... elseif winamp.state = winamp.state.Paused then... elseif winamp.state = winamp.state.Stoped then... 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
Mensajes: 10.683
Yo que tu lo pienso dos veces
|
¿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
Mensajes: 9.866
|
¿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
Mensajes: 10.683
Yo que tu lo pienso dos veces
|
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" 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
Mensajes: 9.866
|
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" 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. ¿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: Private Sub waProcess_Exited(ByVal sender As Object, ByVal e As EventArgs) Handles waProcess.Exited RaiseEvent WinampExited() End Sub
En la class de mi form: Private Sub Exited() Handles Winamp.WinampExited Winamp_IsRunning = False End Sub Private Sub Monitor_Timer_Tick(sender As Object, e As EventArgs) Handles Monitor_Timer.Tick If Not Winamp_IsRunning Then Winamp_IsRunning = Winamp.Bind() End sub
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
Mensajes: 10.683
Yo que tu lo pienso dos veces
|
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, using System; using System.Diagnostics; using System.Timers; namespace ConsoleApplication6 { class Program { static void Main(string[] args) { Timer timer = new Timer (50); //Para una aplicacion de consola toca usar este Timer //http://msdn.microsoft.com/en-us/magazine/cc164015.aspx timer .Elapsed += new ElapsedEventHandler (Run ); timer.Start(); Console.ReadLine(); } static void Run(object sender, EventArgs e) { Console.WriteLine(DateTime.Now); for (int i = 0; i < 2000; i++) { Process.GetProcesses(); } } } }
El código es muy simple, pero si lo ejecutas verás como la RAM va aumentando progresivamente 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
Mensajes: 9.866
|
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: Public Class Form1 Dim isbinded As Boolean = False Dim Winamp As clsWACC = New clsWACC Dim WithEvents mytimer As New Timer With {.Interval = 50, .Enabled = True} Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles mytimer.Tick isbinded = Winamp.Bind() ' Label1.Text = isbinded ' isbinded = Nothing End Sub 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
Mensajes: 9.866
|
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
|
|
|
|
|
Mensajes similares |
|
Asunto |
Iniciado por |
Respuestas |
Vistas |
Último mensaje |
|
|
Punto de fuga
Diseño Gráfico
|
pumass
|
0
|
1,742
|
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,268
|
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,862
|
24 Diciembre 2014, 09:08 am
por Eleкtro
|
|
|
Qué es una fuga de memoria RAM y cómo arreglarla
Noticias
|
wolfbcn
|
0
|
1,387
|
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
|
7,387
|
25 Abril 2020, 18:13 pm
por Usuario887
|
|