2012-05-27 17 views
9

Buenas tardes a todos!MakeScreenshot goteando?

En un proyecto actual, estoy experimentando una fuga de memoria bastante preocupante que parece que no puedo conectar.

Dejé la aplicación ejecutándose durante la noche con el uso estándar y cuando desperté 8 horas después, estaba consumiendo ~ 750MB de memoria mientras que comenzaba en ~ 50MB. El Administrador de tareas de Windows no es adecuado para verificar fugas que no sean para permitirle saber que existe una en primer lugar.

He aclarado algunas otras pérdidas de memoria ya, la principal está relacionada con Firemonkeys 'TGlowEffect. No es detectado por ReportLeaksOnShutdown pero el uso de la memoria se vuelve extremadamente excesivo en objetos modificados dinámicamente (por ejemplo, cambios de rotación o escala).

Lo he rastreado a un temporizador (y deshabilitarlo detiene por completo la fuga), y necesito ayuda para solucionarlo si es posible.

Descripción: Este código utiliza la función FireMonkey MakeScreenshot para guardar la apariencia visual de un TPanel (SigPanel) a un TMemoryStream. Esta información de flujo se carga luego en un servidor FTP remoto utilizando un código estándar (ver a continuación). Dentro de SigPanel, hay 4 TLabel niños, 1 TRectangle niño y 6 TImage niños.

Notas:CfId es una cadena global y se genera en base a un valor aleatorio extended flotador que se hash a continuación, junto con el DateTime en formato yyyymmdd_hhnnsszzz. Esta generación se realiza cuando se crea el formulario y se repite hasta que obtiene un CfId válido (es decir, no contiene caracteres ilegales para su uso en nombres de archivos de Windows). Una vez que obtiene un CfId válido, no se ejecuta de nuevo (ya que no hay necesidad de que genere una nueva ID). Esto me permite eliminar casi por completo la posibilidad de duplicar CfId.

El código en el temporizador es el siguiente;

var 
    i : Integer; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 
    if VT2SigUp.Connected then 
    begin 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end else 
    begin 
    VT2SigUp.Connect; 
    VT2SigUp.Put(SigStream,'Sig_'+CfId+'.png',False); 
    end; 
    SigStream.Free; 
end; 

Con el temporizador NO en funcionamiento, el código funciona completamente sin fugas y ReportMemoryLeaksOnShutdown hace NO generar un mensaje. Con el temporizador habilitado y se le permite "correr" al menos una vez, estoy obteniendo una gran cantidad de fugas que aumentan cuantas más veces se ejecute el temporizador. Las filtraciones reportadas son las siguientes;

Small Block Leaks 

1 - 12 Bytes: Unknown x 1 
13 - 20 Bytes: TList x 5, Unknown x 1 
21 - 28 Bytes: TFont x 2, TGradientPoint x 8, TGradientPoints x 4, Unknown x 4 
29 - 36 Bytes: TObjectList<FMX.Types.TCanvasSaveState> x 1, TBrushBitmap x 4, 
TBrushGrab x 4, TPosition x 24, TGradient x 4, UnicodeString x1 
37 - 44 Bytes: TBrushResource x 4 
53 - 60 Bytes: TBrush x 4 
61 - 68 Bytes: TBitmap x 5 
69 - 76 Bytes: TD2DCanvasSaveState x 1 
205 - 220 Bytes: TCanvasD2D x 1 

Sizes of Medium and Large Block Leaks 
200236 

Como el funcionamiento del temporizador, estos valores se multiplican n veces (n siendo el número de veces que el temporizador ha de ejecución). Los bloques medianos y grandes tienen n valor de 200236 (por ejemplo, si el temporizador se ha ejecutado 3 veces, es 200236, 200236, 200326).

De interés, si elimino el código asociado con MakeScreenshot, la fuga ya no existe y el uso de la memoria se mantiene en un nivel algo normal. Además del uso habitual de la memoria, no hay nada fuera de lo común y no se informan las fugas. Probé varias muestras de código, tanto con guardar en una transmisión y cargar desde allí, o guardar en transmisión> Archivo y luego subir el archivo, pero parece que hay una fuga dentro de la función.Incluso agregué MakeScreenshot.Free una vez que descubrí una fuga aquí, pero simplemente parece que no puedo conectarla, y por supuesto, he usado try..finally en uno de mis "análisis de prueba" de código.

Incluso he ejecutado el código con GDI + como el tipo de lienzo y la misma fuga se produce allí (con el único cambio que las fugas D2D hacen referencia a GDI + en su lugar).

Agradecería cualquier investigación o notas que alguien tenga sobre esto y, además, una solución al problema.

+1

Creo que acabas de encontrar una pérdida de memoria en FM (: – ComputerSaysNo

+0

Creo que la configuración 'ReportMemoryLeaksOnShutdown: = True;' en la inicialización de su aplicación debe hacer el truco para mostrarle lo que se está filtrando ... –

+0

@DorinDuminica Creo que sí. Sin embargo, creo que he encontrado que el 'Resultado' en 'FMX.Types.MakeScreenshot' no es realmente liberados. Simplemente están llamando a 'Result.Canvas.EndScene' y ¡nunca lo liberaron! @Je rryDodge Eso es lo que corrí para descubrir lo que se estaba filtrando exactamente, ya que no habría podido obtener una lista precisa sin él :) –

Respuesta

13

No está liberando el mapa de bits creado por MakeScreenshot.

procedure TForm1.Button1Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
begin 
    ms := TMemoryStream.Create; 
    Panel1.MakeScreenshot.SaveToStream(ms); 
    ms.Free; 
end; 

El código anterior no guarda una referencia al mapa de bits creado, por lo tanto no tiene posibilidad de liberarlo. En su lugar cambiar su diseño como el siguiente:

procedure TForm1.Button2Click(Sender: TObject); 
var 
    ms: TMemoryStream; 
    bmp: TBitmap; 
begin 
    ms := TMemoryStream.Create; 
    bmp := Panel1.MakeScreenshot; 
    bmp.SaveToStream(ms); 
    ms.Free; 
    bmp.Free; 
end; 


Con el código de abajo usted es, de hecho, la creación de dos mapas de bits y la liberación de uno de ellos.

SigPanel.MakeScreenshot.SaveToStream(SigStream); 
    SigPanel.MakeScreenshot.Free; 


Al final, el código sería más como el siguiente:

var 
    i : Integer; 
    Bmp: TBitmap; 
    SigStream : TMemoryStream; 
begin 
    SigStream := TMemoryStream.Create; 
    try 
    Bmp := SigPanel.MakeScreenshot; 
    try 
     Bmp.SaveToStream(SigStream); 
     if not VT2SigUp.Connected then 
     VT2SigUp.Connect; 
     VT2SigUp.Put(SigStream, 'Sig_'+CfId+'.png', False); 
    finally 
     Bmp.Free; 
    end; 
    finally 
    SigStream.Free; 
    end; 
end; 
+2

Complementando @Sertac answear no se olvide de utilizar la prueba finalmente para evitar errores en el put y evitar la fuga de la corriente de memoria, ya que SigStream.Free no se invocará si se produce alguna excepción. –

+0

Ah, efectivamente, eso es todo. Parece una manera tremendamente larga de entenderlo e inicialmente parecía que el resultado no se había liberado en 'FMX.Types'. Supuse que se debe llamar a 'MakeScreenshot' y se le debe dar una referencia (por ejemplo, una transmisión), pero al ver cómo lo ha hecho, me hace comprender cómo se debe usar la función correctamente. Como dije, utilicé 'try..finally' en uno de mis arreglos de código, pero lo saqué para tratar de simplificar el código tanto como sea posible para solucionar el problema. –

+2

@Scott - Está bien omitir el manejo de errores al publicar una pregunta aquí o mientras estás haciendo el diseño inicial. Simplemente no quería dejarlo afuera cuando lo mencioné en los comentarios. –

Cuestiones relacionadas