Утечка памяти в WebObjects
Я боролся с проблемой, с которой у меня довольно долго было использование EOF-фреймворка Apple. Кажется, что иногда, когда объект EOEnterpriseObject создается или извлекается в контекст редактирования из БД, EOF не освобождает память, которую использует объект, даже после удаления соответствующего корпоративного объекта, контекста редактирования и хранилища объектов и удалил. Кажется, что большинство объектов обрабатываются EOF очень хорошо, но у меня есть 2 объекта, где EOF постоянно удерживает память, используемую объектами, до тех пор, пока приложение не будет перезапущено. Оба этих EO потенциально очень большие (они содержат объект NSData, который используется для хранения вложения файла).
Используя JProfiler, я обнаружил, что ссылка на проблемные EO содержится в массиве EODatabase._snapshots.
Мне было интересно, если бы у кого-то еще была похожая проблема с EOF и / или проектом Wonder. Поскольку я последовательно вижу проблему в двух разных сценариях, я надеюсь, что она несколько распространена и, следовательно, имеет решение.
Я использую последнюю библиотеку WebObjects (5.4.3) и новейшие библиотеки Wonder.
Ниже приведен не мой точный код, но это наименьший возможный пример с утечкой памяти:
public WOActionResults createEmailHistoryEntry() throws MessagingException, IOException {
File emailFile = new File("Email_with_large_attachment.eml");
javax.mail.Message message = EmailUtils.convertEmlToMessage( emailFile );
EOObjectStore osc = new ERXObjectStoreCoordinator(true);
EOEditingContext ec = ERXEC.newEditingContext(osc);
ec.lock();
try {
EmailHistoryEntry historyEntry = (EmailHistoryEntry) EOUtilities.createAndInsertInstance( ec, EmailHistoryEntry.class.getSimpleName() );
EmailDataObject emailData = (EmailDataObject) EOUtilities.createAndInsertInstance( ec, EmailDataObject.class.getSimpleName() );
emailData.setEmailHistoryEntry( historyEntry );
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
message.writeTo( byteStream );
NSData rawEmail = new NSData( byteStream.toByteArray() );
emailData.setRawEmail( rawEmail );
ec.saveChanges();
}
finally {
ec.unlock();
ec.dispose();
osc.dispose();
}
return null;
}
Я не знаю, что я делаю там что-нибудь необычное. Если я запускаю его несколько раз, потребление памяти будет увеличиваться примерно на 140 МБ каждый раз, и в итоге возникнет ошибка OutOfMemory.
2012-12-26 Редактировать
Я провел еще несколько расследований по этому вопросу. Похоже, что проблема в библиотеке Project Wonder, а не в библиотеке EOF. Я понимаю, что "проблема", скорее всего, связана со мной и / или моим пониманием, а не с Wonder lib.:)
Я создал тестовое приложение, которое дублирует проблему, с которой я столкнулся, и разместил ее на github: https://github.com/t-evans/memory-leak-test.git.
Тестовое приложение - это в основном приложение по умолчанию, которое Eclipse создает при добавлении нового приложения Wonder. Изменения включают добавление одной строки в Application.java, большей части кода в Main.java и, конечно же, файла модели. В настоящее время он настроен для подключения к базе данных postgres с именем "memleaktest".
Конфигурация запуска моего приложения имеет только два аргумента VM: "-Xmx5m -Xmx50m". Если я запускаю приложение и нажимаю ссылку "Создать объект" примерно 5 раз, оно вызывает ошибку OutOfMemory. Мониторинг памяти с помощью jConsole показывает, что потребление памяти увеличивается примерно на 5 МБ каждый раз, и приложение никогда не отпускает эти 5 МБ.
Мои выводы пока указывают на то, что виновником является ERXObjectStoreCoordinatorSynchronizer. В тестовом приложении Application.java включает синхронизацию. Конструктор Main.java просто выполняет фиктивный запрос, который в конечном итоге заставляет Main._osc быть переданным ERXObjectStoreCoordinatorSynchronizer.addObjectStore() (синхронизатору требуется более 1 OSC для синхронизации чего-либо). Main.createDataStore() создает OSC и EC, добавляет объект DataStore в БД, а затем обнуляет OSC и EC.
ПОСЛЕ того, как новый объект, OSC и EC обнулены, удалены и выпадают из области видимости, синхронизатор запускается и добавляет этот вновь созданный (но теперь уже устаревший) объект к этому другому OSC, что в конечном итоге повторно добавляет новый объект в массив EODatabase._snapshots, где он остается до тех пор, пока не будет удален другой OSC.
Кажется странным, что новый EO после него синхронизируется с остальными OSC, а это EC и OSC, они мертвы, ушли и вышли из области видимости. Разве синхронизатор не должен также синхронизировать тот факт, что EO находится вне области видимости, и удалить его из всех других OSC (или вообще не добавлять его из других OSC)?
Я знаю, что синхронизацию можно отключить, позвонив
ERXObjectStoreCoordinatorSynchronizer.synchronizer().setDefaultSettings(
new SynchronizerSettings(false, false, false, false));
что позволит избежать этой проблемы, но в настройках по умолчанию для синхронизатора все включено, что вызывает довольно большую утечку.
Это ошибка или я что-то делаю неправильно? Я смущен, почему другие люди, кажется, не сталкиваются с этим. Или, может быть, они сталкиваются с этим, но не заметили утечку памяти, потому что они не используют большие EO (?)
3 ответа
Лучшее решение, которое я нашел, это либо избегать ERXObjectStoreCoordinatorSynchronizer (что означает, что вам также нужно избегать ERXObjectStoreCoordinatorPool, так как он использует синхронизатор), либо отключить синхронизатор следующим образом:
ERXObjectStoreCoordinatorSynchronizer.synchronizer (). SetDefaultSettings(new SynchronizerSettings(false, false, false, false));
Или вы могли бы просто отключить InsertSnapshotProcessor:
ERXObjectStoreCoordinatorSynchronizer.synchronizer (). SetDefaultSettings (new SynchronizerSettings (false, true, true, true));
поскольку именно здесь возникает утечка памяти (другие тоже могут вызывать проблемы, но я этого не видел).
После публикации в почтовой группе Project Wonder, похоже, никто не нашел лучшего решения, чем приведенное выше.
Я надеюсь, что вы могли проверить все части своего кода и профилировать его. Но все же я чувствую, что проблема только в вашем коде.
Стоит еще раз проверить следующее: ссылки на EO, EC, объект NSData, Компоненты и посмотреть, случайно ли где-нибудь ваши огромные объекты и, что более важно, EC не будут допущены к GC-ed.
Нам может понадобиться дополнительная информация, чтобы помочь вам отладить эту проблему, если проблема не устранена!
Извините, что опубликовал это как ответ, но я новичок в Stackru и у меня недостаточно очков, чтобы добавить комментарий. Просто хотел добавить ссылку на проблему в репозиторий Wonder Github, которая решает эту проблему, в надежде, что это поможет привести к решению:
https://github.com/wocommunity/wonder/issues/130 - от пользователя 'nullterminated' (я полагаю, что это Рэмси Герли, который подтвердил проблему - см. http://comments.gmane.org/gmane.comp.web.webobjects.wonder-disc/19078)
"Похоже, что ERXObjectStoreCoordinatorPool утечки объектов EODatabase._DatabaseRecord всякий раз, когда размер пула> 1 и EO сохранены. После многих часов в отладчике, я думаю, я понимаю, почему..."
"Обычно, когда EC сохраняет изменения, генерируется уведомление ObjectsChangedInStore, снимок вставляется с _fastHashInsert базы данных EODatabase, а затем EC повторно устанавливает EO при обработке изменений (обновлений) или освобождает снимок при финализации (вставках). Эти действия запускают соответствующие _fastHash Удалите, чтобы освободить снимок. "
"Похоже, проблема в том, что ERXObjectStoreCoordinatorSynchronizer повторно передает уведомление ObjectChangedInStore другим OSC в пуле. Это приводит к вставке моментальных снимков, но без очистки EC, снимки никогда не удаляются".