SkiaSharp
SkiaSharp copied to clipboard
[BUG] SKObject does not dispose or release memory on linux
Description
When I was using SkiaSharp on Linux, I noticed that the memory footprint didn't go down, it just kept going up with each call. When I tried to reproduce the bug on Windows, the memory usage didn't go up.
Code I used following code to test on Windows11 x64, Debian 10 x64, and Debian 11 arm64.
static int Main()
{
while (true)
{
var key = Console.ReadKey();
switch (key.Key)
{
case ConsoleKey.T:
Test();
break;
case ConsoleKey.D:
TestWithDispose();
break;
case ConsoleKey.G:
GC.Collect();
break;
case ConsoleKey.Q:
return 0;
default:
break;
}
Console.WriteLine($"{Environment.WorkingSet / 1024 / 1024} MB");
}
}
static void Test()
{
SKBitmap bitmap = SKBitmap.Decode("sample.gif");
SKCanvas sKCanvas = new SKCanvas(bitmap);
return;
}
static void TestWithDispose()
{
SKBitmap bitmap = SKBitmap.Decode("sample.gif");
SKCanvas sKCanvas = new SKCanvas(bitmap);
bitmap.Dispose();
sKCanvas.Dispose();
return;
}
Expected Behavior
After every Test()
and GC.collect()
, the memory footprint should be kept low.
Actual Behavior
On Windows it works as it should be.
But on Linux, the memory footprint is always kept at a high level, even when GC.collect()
is called.
Just like those objects are always cached and can't be released.
Basic Information
-
Version with issue: 2.80.3
-
IDE: Visual Studio 2022
-
Platform Target Frameworks:
- Linux: Debian 10 x64, Debian 11 arm64
- Windows Classic: Windows 11 x64
Screenshots
here is my test on Windows
and here is my test on Debian 11 (Raspberry Pi 4B)
Additionally, I did tests with SkiaSharp.NativeAssets.Linux 2.88.0-preview.179 and SkiaSharp 2.88.0-preview.179, which still resulted in consistently increasing memory usage.
I think the problem is related to libSkiaSharp.so, because the problems that have occurred so far have been on the Linux platform.
I don't have a device to test on macOS , if so, I'll update this issue.
Is there any update? Same problem with SkiaSharp 2.80.2 on Ubuntu 20.04.
Are you testing with mono or .net core on linux? I wonder if there is just the fact that mono does not feel the need to clear things or does not reduce the indicated memory? Are you using .net framework or dotnet core on windows?
Are you testing with mono or .net core on linux? I wonder if there is just the fact that mono does not feel the need to clear things or does not reduce the indicated memory? Are you using .net framework or dotnet core on windows?
I'm using dotnet 6.0 both on Windows and Linux.
This bug confused me for weeks, until I found this, which mentioned that this is a problem with memory allocator. https://github.com/dotnet/runtime/issues/13301#issuecomment-535641506
After setting MALLOC_TRIM_THRESHOLD_
, I could control my memory usage in a acceptable range.
But unlike on Windows.and MacOS(i tested it on macos, same as windows) , the memory usage doesn't go down to a low level after calling ,GC.collect()
, it seemed being limited at a certain level.In my case it goes up to as test going, then stuck at 300MB.
For somehow it did fixed my problem, but i wonder if there's a better solution.
I have the same issue with a quite similar setup => .NET 6 and Ubuntu 20.04.
I just load a file from disk, copy it and dispose both instances.
DotMemory shows following:
It seems SKShader hangs arround and will be finalized by the GC. Is this the intended behavior? I expect SkiaSharp to free any resources immediatelly when calling Dispose().
using SkiaSharp;
for (int i = 0; i < 10000; i++)
{
Console.WriteLine($"Run: {i}");
Console.WriteLine("Load skiasharp from file!");
using var filestream = File.Open("navi.PNG", FileMode.Open);
var bitmap = SKBitmap.Decode(filestream);
var copy = bitmap.Copy();
bitmap.Dispose();
copy.Dispose();
}
Edit: if I use my own copy implementation, which disposes the SKShader, the memory spikes are gone and everything is freed up:
SKBitmap CopyFixed(SKBitmap bitmap, SKColorType colorType)
{
using var srcPixmap = bitmap.PeekPixels ();
var temp = new SKBitmap ();
var dstInfo = srcPixmap.Info.WithColorType (colorType);
if (!temp.TryAllocPixels (dstInfo))
return null;
using var canvas = new SKCanvas (temp);
using var shader = bitmap.ToShader();
using var paint = new SKPaint {
Shader = shader,
BlendMode = SKBlendMode.Src
};
canvas.DrawPaint (paint);
return temp;
}
I don't know if there are any edge-cases, in which this change might break something, but for me this is good enough.
This seems related: I kept having problems with the bitmap being in use when calling bitmap.Copy(). This occurred in both Windows and Android. Calling GC.Collect() fixed the 'in use exception'. It would only happened randomly though. about 1/4 of the time. Here is my relevant code:
var info = new SKImageInfo(width + 1, height + 1, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul);
var outbmp = new SKBitmap(info);
var BMArray = new byte[(info.RowBytes * info.Height)];
...load data into the array...
// pin the managed array so that the GC doesn't move it
var handle = GCHandle.Alloc(BMArray, GCHandleType.Pinned);
// install the pixels with the color type of the pixel data
outbmp.InstallPixels(info, handle.AddrOfPinnedObject(), info.RowBytes);
handle.Free();
... Sometime later call
var copyBitmap = outbmp.Copy();
Calling GC.Collect(); after freeing the handle has fixed my issue. So, it seems that after freeing the handle, something was still being held onto the bitmap.
Have the same issue, on Ubuntu 22.04. Looks like calling Dispose is not freeing any resources and memory keeps growing until process is killed in the end.