[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Excessive Memory Consumption in SkiaSharp Library Leading to OOM Crashes #2915

Open
1 task done
luigisaggese opened this issue Jul 2, 2024 · 0 comments
Open
1 task done
Labels

Comments

@luigisaggese
Copy link
luigisaggese commented Jul 2, 2024

Description

Issue Description
Description
Our application is experiencing high memory consumption, leading to Out of Memory (OOM) exceptions. After extensive debugging and analysis with the Microsoft support team, we have identified that the SkiaSharp library is the primary driver of the excessive memory usage.

Steps to Reproduce

  1. Deploy the application using SkiaSharp 2.88.8 in a Docker container with the official .NET SDK 8.0 image.
  2. Run the application under typical load, processing images.
  3. Monitor memory usage over time.

Expected Behavior
Memory usage should remain stable and within acceptable limits, without leading to OOM crashes.

Actual Behavior
Memory usage continually increases over time, ultimately causing the application to crash with an OOM exception. The issue is most prevalent when processing images, which suggests that SkiaSharp's memory management might be the root cause.

Evidence and Analysis

  1. Heap Dumps: Multiple heap dumps indicate significant memory consumption in PAGE_READWRITE regions, which is likely attributed to native memory allocations.
  2. Heaptrack Analysis:
  • Peak memory consumers show SkiaSharp's native library (libSkiaSharp.so) consuming large amounts of memory.
  • At its peak, SkiaSharp allocated ~250MB of RAM over 1257 calls, with specific functions like sk_bitmap_try_alloc_pixels being notable contributors.

Environment

  • SkiaSharp Version: 2.88.8
  • .NET Version: 8.0.5
  • OS: Linux Debian (Docker Container)
  • Image: mcr.microsoft.com/dotnet/sdk:8.0

Relevant Files

  • ImageHelper.cs: The primary usage of SkiaSharp within our application.
  • Heap Dumps and Heaptrack Traces: Available upon request.

Any advice on configuration changes or code adjustments to mitigate the issue would be appreciated.
Updates: Information on any known issues or updates in SkiaSharp that address this problem.

Code

public class ImageHelper : IImageHelper
    {
        public byte[]? ResizeImage(byte[] img, Size maxSize)
        {
            try
            {
                // If the size is 0, return the default image size
                if (maxSize.IsEmpty)
                    return img;

                using MemoryStream ms = new MemoryStream(img);

                using SKCodec skCodec = SKCodec.Create(ms);
                using SKBitmap sourceBitmap = SKBitmap.Decode(skCodec);

                // Rotate if needed
                using SKBitmap rotatedBitmap = HandleOrientation(sourceBitmap, skCodec.EncodedOrigin);

                // Calculate the new size
                var newSize = CalculateNewSize(maxSize, new Size(rotatedBitmap.Width, rotatedBitmap.Height));

                SKFilterQuality quality = SKFilterQuality.Medium;
                using SKBitmap scaledBitmap = rotatedBitmap.Resize(new SKImageInfo(newSize.Width, newSize.Height), quality);

                using SKImage scaledImage = SKImage.FromBitmap(scaledBitmap);
                using SKData data = scaledImage.Encode(SKEncodedImageFormat.Jpeg, 75);

                return data?.ToArray();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error while resising the picture: {ex}");
                return null;
            }
        }

        private static SKBitmap HandleOrientation(SKBitmap bitmap, SKEncodedOrigin orientation)
        {
            SKBitmap rotated;
            switch (orientation)
            {
                case SKEncodedOrigin.BottomRight:

                    using (var surface = new SKCanvas(bitmap))
                    {
                        surface.RotateDegrees(180, (float)bitmap.Width / 2, (float)bitmap.Height / 2);
                        surface.DrawBitmap(bitmap.Copy(), 0, 0);
                    }

                    return bitmap;

                case SKEncodedOrigin.RightTop:
                    rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                    using (var surface = new SKCanvas(rotated))
                    {
                        surface.Translate(rotated.Width, 0);
                        surface.RotateDegrees(90);
                        surface.DrawBitmap(bitmap, 0, 0);
                    }

                    return rotated;

                case SKEncodedOrigin.LeftBottom:
                    rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                    using (var surface = new SKCanvas(rotated))
                    {
                        surface.Translate(0, rotated.Height);
                        surface.RotateDegrees(270);
                        surface.DrawBitmap(bitmap, 0, 0);
                    }

                    return rotated;

                default:
                    return bitmap;
            }
        }

        private static Size CalculateNewSize(Size maxSize, Size currentSize)
        {
            var newSize = currentSize;

            // Check the height
            if (newSize.Height > maxSize.Height)
            {
                newSize.Height = maxSize.Height;
                newSize.Width = currentSize.Width * newSize.Height / currentSize.Height;
            }

            currentSize = newSize;

            // Check the width
            if (newSize.Width > maxSize.Width)
            {
                newSize.Width = maxSize.Width;
                newSize.Height = newSize.Height * newSize.Width / currentSize.Width;
            }

            return newSize;
        }
    }

Code of Conduct

  • I agree to follow this project's Code of Conduct
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant