0

Running OutOfMemory in my relatively simple web API which is deployed to an Azure server. I download two images using the same method called DownloadImageFromUrl(string url).. I then just draw some text on these, do resizing, and return the image. One of them is a relatively large image file, anywhere from 1-12 MB.

Here is the implementation of my DownloadImageFromUrl, which the error messages (and heap analysis, see below) are pointing me to. Both methods result in the same errors:

  private Bitmap DownloadImageFromUrl(string url)
    {
        //try
        //{
        //    // METHOD A:
        //    WebRequest request = System.Net.WebRequest.Create(url);
        //    WebResponse response = request.GetResponse();
        //    Stream responseStream = response.GetResponseStream();
        //    return new Bitmap(responseStream);
        //}
        //catch (WebException e)
        //{
        //    return null;
        //}
        //METHOD B:
        try
        {
            using (WebClient client = new WebClient())
            {
                byte[] data = client.DownloadData(url);
                using (MemoryStream mem = (data == null) ? null : new MemoryStream(data))
                {
                    return (data == null || mem == null) ? null : (Bitmap)Image.FromStream(mem);
                }
            }
        }
        catch (WebException e)
        {
            return null;
        }
    }

My API handles singular requests very well. The problem comes in when blasting it with requests. When I send 20 all at the same time, the memory shoots up to over 1g and throws OutOfMemoryExceptions. When checking out snapshots of the heap, I notice that there is a new memorystream object created for each API call (20 of them) and it alone is using 30,410,112 bytes at the climax of the memory spike.

Can someone help me to alter my API to handle more user requests at once? Strangely enough, creating more instances of my app on Azure actually yielded worse results.

Edit: I've also considered using ImageMagick.NET in order to handle the drawing and resizing of images but this will require a big overhaul.

JakeD
  • 407
  • 2
  • 7
  • 19
  • Add a lock around the using statement so only one thread creates a memory stream. See : https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx – jdweng Jan 11 '17 at 22:01
  • I think this will work but won't it drastically reduce the performance speed because the 20th request will have to wait for all other 19? – JakeD Jan 11 '17 at 22:06
  • You only have a certain bandwidth of processing. So if you most of the processing is creating the Bitmap then the loss in performance is minimal. – jdweng Jan 11 '17 at 22:16
  • Do you dispose bitmap after usage? – Yuriy Tseretyan Jan 11 '17 at 22:39
  • As a solution, you can decrease size of threadpool available for service to some relatively reliable number. See `applicationPool` in Web.config – Yuriy Tseretyan Jan 11 '17 at 22:45
  • This sounds more like what I need, because rather than locking, I can still use a few threads, just not too many that my application crashes. Will try and report back – JakeD Jan 12 '17 at 14:35
  • Can you give me an example of how to alter this in Web.config? Putting it as a child under system.web is not working for me, and other sources suggest altering an aspnet.config file which i can't find. – JakeD Jan 12 '17 at 14:45
  • 1
    @Jaked222 Check this http://stackoverflow.com/a/7898696/1437693 and this http://stackoverflow.com/a/4571174/1437693. I hope it will help – Yuriy Tseretyan Jan 12 '17 at 17:28
  • Thanks.. It looks like this one is the most relevant: http://stackoverflow.com/questions/4571118/how-to-increase-thread-pool-threads-on-iis-7-0/4571174#4571174 but I have made the change to my aspnet.config file and there is no difference in the performance.. :/ – JakeD Jan 12 '17 at 17:59
  • Dirty workaround is to create a worker that does image processing and pool of workers with specific size. Then if pool is empty, request handler will have to wait until another threads release worker. – Yuriy Tseretyan Jan 12 '17 at 20:22
  • I'm going to try this although I agree it is weird knowing that the processing is already parallelized. I'd have to lock the route first so only one thread at a time can access, then within there create a thread pool... ew – JakeD Jan 12 '17 at 20:39
  • You can create pool with 10 workers (or as many as your memory allows you), and this will allow you to share workers between 10 parallel threads (clients) and serve 10 requests simultaneously. Check for 'ObjectPool` in `ParallelExtensionsExtras` library. – Yuriy Tseretyan Jan 12 '17 at 21:24

0 Answers0