使用axlsx_rails gem在Heroku遇R14内存超限问题求助
Hey there, let’s tackle this memory leak issue with axlsx_rails on Heroku head-on. I’ve helped debug similar problems before, so here’s a step-by-step approach to get to the bottom of it:
Before jumping into code changes, let’s verify that this is indeed a leak (and not just a one-time memory spike):
- Use
heroku logs --tailto watch when R14 errors trigger—note if it happens repeatedly after each report generation, not just once. - Fire up a dyno shell with
heroku ps:exec, then usetoporps auxto monitor your Rails process’s memory usage before and after generating a report. If memory climbs and never drops back down, that’s a clear leak sign. - Check
heroku metricsfor long-term memory trends—you should see a gradual upward slope if objects are being retained instead of garbage-collected.
axlsx can be memory-hungry if not used carefully, especially with large tables. Let’s check your implementation:
- Avoid redundant style objects: If you’re creating new styles inside loops (e.g., in your ERB view), you’re probably piling up unused style objects. Define styles once at the top of your view or in a helper, then reuse them:
# In your axlsx view styles = workbook.styles header_style = styles.add_style(bold: true, alignment: { horizontal: :center }) # Reuse header_style across all header cells instead of recreating it - Check for lingering object references: Make sure you’re not storing the generated
WorkbookorWorksheetobjects in global variables, class variables, or any long-lived objects (like a cache or singleton). These references will prevent Ruby’s GC from cleaning up the memory. - Test with minimal data: Generate a tiny report (even an empty one) and monitor memory. If the leak still happens, the issue is in the core setup, not the data size. If it only happens with large reports, you might be loading too much data into memory at once.
Ruby’s garbage collector might not be cleaning up objects if they’re accidentally being held onto. Let’s dig deeper:
- Enable GC logging: Add
RUBY_GC_LOG_LEVEL=DEBUGto your Heroku environment variables. This will log GC activity to your dyno logs, so you can see if GC is running but not releasing memory, or if it’s not running often enough. - Use a memory profiler: Add the
memory_profilergem to your Gemfile (temporarily, for debugging), then wrap your report generation code to track object allocations:
Use# In your controller or view wrapper report = MemoryProfiler.report do # Code that triggers the axlsx_rails report generation end report.pretty_print(to_file: 'memory_report.txt')heroku ps:copy memory_report.txtto download the file, then look for objects that are allocated but never freed—these are your leak candidates.
Even if your code is clean, Heroku’s dyno environment can have quirks:
- Performance-M dynos have 2.5GB of memory, but if your process’s memory creeps close to that threshold over time, R14 will trigger. Restarting resets the memory, but the leak will build back up.
- Check for other memory-heavy processes: If you’re running Sidekiq or other background workers on the same dyno, they might be contributing. But since you mentioned low traffic at night, this is less likely.
- Reproduce manually: Use
heroku run rails consoleto trigger report generation directly, and watch memory usage in real-time withtop—this makes it easier to isolate the issue without waiting for user traffic.
Once you’ve identified the root cause, try these fixes:
- Batch process large datasets: Instead of loading all records at once with
Model.all, useModel.find_each(batch_size: 1000)to process data in chunks. After each batch, you can manually trigger GC withGC.start(don’t overdo this—only use it if necessary). - Explicitly release references: After generating the report, set any variables holding the workbook/worksheet to
nilto help GC recognize they’re no longer needed. - Update your gems: Check the GitHub issues for
axlsxandaxlsx_rails—there might be known memory leak bugs that have been fixed in newer versions. Upgrading could resolve the issue immediately. - Consider streaming if possible: Some versions of axlsx support streaming large workbooks, which keeps memory usage low by writing data to disk incrementally instead of holding everything in memory.
内容的提问来源于stack exchange,提问作者Andy Simon

