Основные проблемы использования памяти с Prawn на JRuby
У нас были серьезные проблемы с использованием памяти при компиляции больших документов (несколько сотен страниц +) с использованием Prawn с JRuby on Rails и Torquebox.
Наше приложение в основном позволяет пользователям загружать кучу документов PDF и объединяет их в один. Часть Prawn берет список файлов, перебирает каждый из них постранично, вставляет каждую страницу в наш выходной документ через document.start_new_page(template: 'filename')
и делает некоторые незначительные добавления (рисование номеров страниц и вкладок) к каждой странице после ее вставки. Часть соответствующего кода скопирована ниже.
Хотя некоторые из файлов, которые мы генерируем, довольно велики, использование памяти все еще кажется чрезмерным. Например, для создания файла размером 38 МБ потребовалось более 4 ГБ ОЗУ (!). Этот конкретный файл был создан путем сопоставления 30 файлов, самые большие из которых имеют размер 7 МБ и 5 МБ, а большинство - всего несколько 100 КБ - но нам пришлось увеличить размер кучи Java до 5 ГБ, чтобы предотвратить падение сервера.
Должны ли мы удивляться, что наш объем использования памяти так высок? Мы делаем что-то не так, и есть ли решение? Большое спасибо.
Настройка нашего сервера:
Версия JRuby: 1.7.4 Версия Rails: 4.0.0, работает в рабочем режиме Torquebox: 3.0.0 ОС: Ubuntu 12.04 Версия LTS Prawn: 1.0.0rc2 JAVA_OPTS: -server -XX:+UseCompressedOops -Xms512m -Xmx5000m -XX:MaxPermSize=512m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
Вот самая важная часть кода: add_file()
вызывается один раз для каждого входного файла. Обратите внимание, что есть вызов @document.start_new_page()
, который фактически вставляет новую страницу, а затем вызов start_new_page()
, который не вставляет новую страницу, но гарантирует, что page.dictionary.date
правильно установлено. Я не знаю, имеет ли это отношение к нашей проблеме с памятью.
def create_or_update_document(opts)
if @document
@document.start_new_page(opts)
else
opts[:margin] = 0
@document = Prawn::Document.new(opts)
@document.font_size 12
end
end
def add_file(file)
puts " add_file() filename=#{file['full_path']}"
filename = file['full_path']
num_pages = file['pagecount']
(1..num_pages).each do |page_number|
puts " Adding page: #{page_number}"
# take the page defined in file 'filename' and insert it into the
# @document we're building
create_or_update_document(:template => filename, :template_page => page_number)
# this call to start_new_page doesn't change our @document, but
# it ensures that 'page.dictionary.data' contains the settings
# (:Rotate, etc) from the source PDF
start_new_page(:template => filename, :template_page => page_number)
puts " Page size: width=#{page_width} height=#{page_height} Rotate: #{page.dictionary.data[:Rotate]}"
# add destination for internal links e.g. from table of contents or tabs
@document.add_dest("page#{@current_page_number}", @document.dest_fit)
rotation = page.dictionary.data[:Rotate]
case rotation
when 90
translate_x = 0
translate_y = -page_width
page_number_x = page_height - RightMargin
tab_page_height = page_width
when 180
translate_x = -page_width
translate_y = -page_height
page_number_x = page_width - RightMargin
tab_page_height = page_height
when 270
translate_x = -page_height
translate_y = 0
page_number_x = page_height - RightMargin
tab_page_height = page_width
else
translate_x = 0
translate_y = 0
rotation = 0
page_number_x = page_width - RightMargin
tab_page_height = page_height
end
@document.rotate(rotation, :origin => [0,0]) do
@document.translate(translate_x,translate_y) do
if @pack.include_tabs
add_tabs(tab_page_height, page_number_x, @current_page_number)
end
@document.text_box "<font name='Georgia' size='12'>#{@current_page_number}</font>", at: [page_number_x, BottomMargin + 14], inline_format: true, width: RightMargin + 100
end
end
@current_page_number = @current_page_number + 1
end
end