mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.52k stars 540 forks source link

[BUG] Memory leak occurs while measuring the text using font stream #2446

Open Ramaraj-Marimuthu opened 1 year ago

Ramaraj-Marimuthu commented 1 year ago

Description

Memory keep on increasing while measuring the text using font stream

Code

for (int i = 0; true; i++)
{
    Console.WriteLine(i);
    // Memory leak occurs while measuring the text using font stream.
    MeasureTextUsingFontStream();
}

// Measure the text using font stream
static void MeasureTextUsingFontStream() {
    using (var stream = File.OpenRead(@"../../../arial.ttf")) {
        using (var typeface = SKTypeface.FromStream(stream)) {
            using (var paint = new SKPaint { Typeface = typeface }) {
                // Issue doesn't occur if we commented the below line.
                var width = paint.MeasureText("Hello, world!");
                paint.Dispose();
            }
            typeface.Dispose();
        }
        stream.Dispose();
    }    
    GC.Collect();
    GC.WaitForPendingFinalizers();
}

Note: You can use any available TTF font to replicate this problem.

Expected Behavior

Allocated memory should be disposed properly

Actual Behavior

Memory allocation increasing rapidly without any disposal

Basic Information

**IDE:** Microsoft Visual Studio Professional 2022 (64-bit) - Preview Version 17.6.0 Preview 1.0 **OS:** OS - Windows 10 Pro Processor - Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz 2.71 GHz Installed RAM - 16.0 GB (15.9 GB usable) System type - 64-bit operating system, x64-based processor ``` PASTE ANY DETAILED VERSION INFO HERE ```

Screenshots

Measuring the text using font stream: (Memory keep on increasing here)

image

Measuring the text using installed font: (Memory disposed properly here and it's maintained in same level)

image

Reproduction Link

Please use the attached sample to replicate the same problem, SkiaSharpTesting.zip

Gillibald commented 1 year ago

Could you try to create the typeface outside the loop? You will most likely not see any increase in memory usage. Skia is caching glyphs data and is only clearing the cache if it reaches some defined memory usage level.

Ramaraj-Marimuthu commented 1 year ago

@Gillibald, Thanks for the update.

Yes, as you mentioned memory leak doesn’t occur if we create the typeface outside the loop.

But for our requirement, we need to create a typeface for every looping. So, is there any possibility to forcibly dispose the memory by using any API’s?

Ramaraj-Marimuthu commented 1 year ago

@Gillibald & @mattleibow - Any solution for this?

It would be very helpful for us.

FoggyFinder commented 1 year ago

But for our requirement, we need to create a typeface for every looping

Why do you need it though?

Ramaraj-Marimuthu commented 1 year ago

@FoggyFinder - Please find the below requirement details,

We are working on the Word document to PDF conversion project, and we are using the SkiaSharp library to measure the width and height of the text by using an embedded font stream in the document.

While converting multiple (more than 100) Word documents to PDF in for loop, memory keeps on growing until the loop ends due to creating more number of SKTypeface for each Word document. Since each Word document may have a different set of embedded font streams, it’s not possible to create SKTypeface commonly (outside of the loop).

To avoid this memory leak problem, we expected to dispose the SKTypeface allocated memory after completing each conversion. Could you please help us to solve this memory problem?

Note: At certain stage, app crashed with out of memory problem.

Ramaraj-Marimuthu commented 1 year ago

@FoggyFinder & @Gillibald & @mattleibow - Any help would be much more appreciated.

themcoo commented 1 year ago

SkTypefaceCache is not exposed in skia sharp, but the purge can be called via SkGraphics:

SKGraphics.PurgeFontCache();

You can also set your own limits in the cache via

SKGraphics.SetFontCacheCountLimit
SKGraphics.SetFontCacheLimit
Ramaraj-Marimuthu commented 1 year ago

@themcoo - Huge thanks for your solution. It's solved the problem in attached simple app.

Will try the same in our project and will update further details.

Thanks again @themcoo

afruzan commented 1 month ago

I had same memory leak issue when using SKTypeface.FromFile. Using SKGraphics.PurgeFontCache(); didn't solve my issue.

At the end I solved memory leak by encapsulating and caching output of SKTypeface.FromFile in a static class.

 public static SKTypeface? GetTypefaceFromFile(string filename)
 {
     return typefaceCache.GetOrAdd(filename, filename => SKTypeface.FromFile(filename) ?? null);
 }