DEV Community

IronSoftware
IronSoftware

Posted on

Aspose PDF Azure OutOfMemoryException: Why It Fails (Fixed)

Developers using Aspose.PDF in Azure App Service encounter OutOfMemoryException even when Azure reports only 40% memory usage. The same code works locally on development machines with ample RAM but crashes in Azure's constrained environments. With over 24,000 views on Stack Overflow, this is a common production issue. This article documents the behavior and examines alternatives that handle Azure's memory constraints more gracefully.

The Problem

Aspose.PDF can throw OutOfMemoryException in Azure App Service while the App Service plan shows significant available memory. This counterintuitive behavior occurs because:

  1. Azure App Service has per-process memory limits that differ from total plan memory
  2. Aspose.PDF's memory allocation patterns can fragment the managed heap
  3. .NET's garbage collector behavior differs between development and production environments
  4. Azure's memory metrics don't reflect native memory usage accurately

The result is production failures that cannot be reproduced locally.

Error Messages and Symptoms

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
   at Aspose.Pdf.Document..ctor(Stream input)
   at YourApp.Controllers.PdfController.Generate()
Enter fullscreen mode Exit fullscreen mode

From developer reports:

OutOfMemoryException even though Azure Service Plan reached only 40% of memory used.
Enter fullscreen mode Exit fullscreen mode

Symptoms include:

  • Application works with 4GB RAM locally but fails with 4GB in Azure
  • Failures are intermittent and hard to reproduce
  • Scaling up the App Service plan may not resolve the issue
  • Memory appears available but allocations fail

Who Is Affected

This issue impacts Azure deployments using Aspose.PDF:

Platforms: Azure App Service (all tiers), Azure Functions, Azure Container Instances.

Framework Versions: .NET Framework 4.x and .NET Core/.NET 5+ deployments.

Use Cases: PDF generation, PDF merging, PDF-to-image conversion, any operation that allocates significant memory.

Evidence from the Developer Community

Timeline

Date Event Source
2019-12-13 Azure OutOfMemoryException reported on Stack Overflow Stack Overflow
2020-03-15 Multiple answers confirm similar experiences Stack Overflow
2022-10-24 Issue continues with 24K+ views Stack Overflow

Community Reports

"I hit the OutOfMemoryException on the using statement even though Azure Service Plan reached only 40% of memory used."
— Developer, Stack Overflow, December 2019

"The same code that works fine locally with 8GB RAM fails in Azure with a Premium P2 plan."
— Developer, Stack Overflow, February 2020

Azure App Service Tier Memory Analysis

Understanding how Azure App Service allocates memory is critical for diagnosing these issues:

Memory Limits by Tier

App Service Tier Total Memory Per-Process Limit Recommended For
Basic B1 1.75 GB ~1.0 GB Development only
Basic B2 3.5 GB ~1.8 GB Light PDF workloads
Basic B3 7 GB ~3.5 GB Moderate workloads
Standard S1 1.75 GB ~1.0 GB Not for PDF generation
Standard S2 3.5 GB ~1.8 GB Light production
Standard S3 7 GB ~3.5 GB Moderate production
Premium P1v3 8 GB ~4 GB PDF-heavy workloads
Premium P2v3 16 GB ~8 GB Large document processing
Premium P3v3 32 GB ~16 GB Enterprise batch processing

Note: Per-process limits are approximate and vary based on Azure's resource allocation. The "40% memory" failure occurs because your process hits its per-process limit while the overall tier memory appears available.

Azure Functions Memory Considerations

Azure Functions imposes additional constraints:

Consumption Plan:

  • Maximum memory: 1.5 GB
  • Timeout: 10 minutes (maximum)
  • Not suitable for PDF generation with Aspose

Premium Plan (EP1-EP3):

  • EP1: 3.5 GB memory
  • EP2: 7 GB memory
  • EP3: 14 GB memory
  • Better suited for PDF workloads but still hits per-instance limits

Dedicated Plan:

  • Uses App Service pricing
  • Same per-process limits apply

Azure Container Apps

For containerized deployments, Azure Container Apps provides different constraints:

# container-app.yaml
properties:
  configuration:
    activeRevisionsMode: Single
  template:
    containers:
    - name: pdf-service
      image: your-registry.azurecr.io/pdf-service:latest
      resources:
        cpu: 2.0
        memory: 4Gi  # Container memory limit
      env:
      - name: DOTNET_GC_SERVER
        value: "1"
Enter fullscreen mode Exit fullscreen mode

Container Apps allow more granular memory control but Aspose.PDF's allocation patterns still cause fragmentation issues within the container's memory space.

Root Cause Analysis

The issue stems from how memory works in Azure App Service:

  1. Per-Process Limits: Azure App Service imposes per-process memory limits that may be lower than the total plan memory. A 4GB plan might only allow 1.5GB per process.

  2. Memory Fragmentation: Aspose.PDF allocates and deallocates memory in patterns that can fragment the managed heap. Large contiguous allocations may fail even with "available" memory.

  3. Native Memory: Aspose.PDF uses native components whose memory usage is not reflected in .NET memory metrics. Azure's memory reporting may not capture this accurately.

  4. Garbage Collection Mode: Azure uses Server GC by default, which behaves differently from Workstation GC used in development. Memory may not be reclaimed as aggressively.

Diagnosing with Azure Monitor and Application Insights

To identify when your application is hitting memory limits, configure Azure monitoring:

Application Insights Setup

Add the Application Insights SDK to track memory metrics:

// Program.cs
using Microsoft.ApplicationInsights.Extensibility;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddApplicationInsightsTelemetry();

// Custom memory tracking
builder.Services.AddSingleton<ITelemetryInitializer, MemoryMetricInitializer>();

public class MemoryMetricInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var process = Process.GetCurrentProcess();
        telemetry.Context.GlobalProperties["WorkingSetMB"] =
            (process.WorkingSet64 / 1024 / 1024).ToString();
        telemetry.Context.GlobalProperties["PrivateMemoryMB"] =
            (process.PrivateMemorySize64 / 1024 / 1024).ToString();
    }
}
Enter fullscreen mode Exit fullscreen mode

Azure Monitor Alerts

Create alerts for memory conditions:

{
  "alertRule": {
    "name": "HighMemoryUsage",
    "condition": {
      "allOf": [
        {
          "metricName": "MemoryWorkingSet",
          "operator": "GreaterThan",
          "threshold": 1500000000,
          "timeAggregation": "Average"
        }
      ]
    },
    "actions": {
      "actionGroups": ["your-action-group-id"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Kusto Query for Memory Analysis

Use this query in Azure Log Analytics to identify memory patterns:

requests
| where timestamp > ago(24h)
| where name contains "pdf" or name contains "document"
| extend memoryMB = toint(customDimensions["WorkingSetMB"])
| summarize avgMemory = avg(memoryMB), maxMemory = max(memoryMB),
            failureRate = countif(success == false) * 100.0 / count()
    by bin(timestamp, 5m)
| order by timestamp desc
Enter fullscreen mode Exit fullscreen mode

Attempted Workarounds

Workaround 1: Scale Up the App Service Plan

Approach: Move to a higher tier App Service plan with more memory.

Basic B2 (3.5GB) → Standard S3 (7GB) → Premium P3 (14GB)
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Increases costs significantly
  • Per-process limits may still apply
  • Does not address root cause
  • Memory usage grows with document complexity

Workaround 2: Use 64-bit App Service

Approach: Ensure the App Service is configured for 64-bit.

Configuration → General settings → Platform: 64-bit
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Only helps if 32-bit was the constraint
  • Does not address per-process limits
  • May not resolve fragmentation issues

Workaround 3: Force Garbage Collection

Approach: Manually trigger garbage collection after PDF operations.

using (var doc = new Aspose.Pdf.Document(stream))
{
    // Process document
}
GC.Collect();
GC.WaitForPendingFinalizers();
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Performance overhead
  • Does not reclaim native memory
  • Not recommended as a standard practice

A Different Approach: IronPDF

IronPDF handles memory differently by using a Chromium subprocess for rendering, isolating memory usage from the main application process.

Why IronPDF Works Better in Azure

IronPDF's subprocess architecture provides benefits in constrained environments:

  1. Process Isolation: Chromium's memory is in a separate process, not counted against the main app's limits
  2. Clean Termination: Subprocess memory is fully released after each operation
  3. No Fragmentation: Each render gets fresh memory space
  4. Predictable Usage: Memory consumption is consistent between development and production

Code Example

using IronPdf;

public class AzurePdfService
{
    public byte[] GeneratePdf(string html)
    {
        // Works consistently in Azure App Service
        var renderer = new ChromePdfRenderer();

        // Memory used by Chromium is isolated from the app process
        using var pdf = renderer.RenderHtmlAsPdf(html);

        return pdf.BinaryData;
        // Chromium subprocess memory is released here
    }

    public byte[] GenerateInvoice(InvoiceData invoice)
    {
        var renderer = new ChromePdfRenderer();

        renderer.RenderingOptions.MarginTop = 20;
        renderer.RenderingOptions.MarginBottom = 20;

        string html = $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: Arial, sans-serif; }}
        .invoice-header {{ background: #0078d4; color: white; padding: 20px; }}
        table {{ width: 100%; border-collapse: collapse; }}
        th, td {{ padding: 10px; border-bottom: 1px solid #ddd; }}
        .total {{ font-size: 1.5em; font-weight: bold; text-align: right; }}
    </style>
</head>
<body>
    <div class='invoice-header'>
        <h1>Invoice #{invoice.Number}</h1>
    </div>
    <table>
        <thead>
            <tr><th>Description</th><th>Amount</th></tr>
        </thead>
        <tbody>
            {GenerateLineItems(invoice.Items)}
        </tbody>
    </table>
    <div class='total'>Total: ${invoice.Total:F2}</div>
</body>
</html>";

        using var pdf = renderer.RenderHtmlAsPdf(html);
        return pdf.BinaryData;
    }

    private string GenerateLineItems(IEnumerable<LineItem> items)
    {
        return string.Join("", items.Select(i =>
            $"<tr><td>{i.Description}</td><td>${i.Amount:F2}</td></tr>"));
    }
}
Enter fullscreen mode Exit fullscreen mode

Azure App Service configuration:

// No special memory configuration needed
// Works on Basic B1 tier for most use cases
Enter fullscreen mode Exit fullscreen mode

Key points about this code:

  • Memory behavior is consistent between local and Azure
  • Chromium subprocess handles heavy memory operations
  • No per-process limit issues for the main application
  • Works on smaller App Service tiers

API Reference

For Azure deployment:

Migration Considerations

Licensing

  • IronPDF is commercial software with perpetual licensing
  • Free trial available for evaluation
  • Licensing information

API Differences

  • Aspose.PDF: Object-oriented API with Document, Page objects
  • IronPDF: HTML-first rendering approach
  • Migration involves changing document generation from Aspose objects to HTML templates

What You Gain

  • Predictable memory behavior in Azure
  • Works on smaller App Service tiers
  • No mysterious OutOfMemoryExceptions at 40% usage

What to Consider

  • Different document generation paradigm
  • Chromium binaries increase deployment size
  • Commercial licensing required

Conclusion

Aspose.PDF's memory usage patterns can cause OutOfMemoryException in Azure App Service even when Azure reports available memory. The issue stems from per-process limits, memory fragmentation, and native memory that isn't tracked accurately. For Azure deployments where memory predictability is important, subprocess-based rendering approaches provide more consistent behavior.


Written by Jacob Mellor, who leads technical development at Iron Software.


References

  1. Stack Overflow: System.OutOfMemoryException in Azure but not locally{:rel="nofollow"} - 24K+ views

For the latest IronPDF documentation and tutorials, visit ironpdf.com.

Top comments (0)