У меня есть служба Windows, которая выполняет некоторые работы по обслуживанию. Недавно мы добавили задание, которое пытается предварительно рассчитать какой-либо результат поиска с использованием Lucene , и с тех пор оно начало выдавать исключения OutOfMemory (OOM).
Некоторые подробности, которые я получил от WinDbg и SOS :
0:034> !analyzeoom
Managed OOM occured after GC #176014 (Requested to allocate 2621440 bytes)
Reason: Low on memory during GC
Detail: SOH: Failed to reserve memory (16777216 bytes)
! Dumpheap -stat результат команды (последние)
65fe4944 81900 34614564 System.Byte[]
65fe2938 76014 35904328 System.Int32[]
65f96064 74 39988372 System.Int64[]
65fdf9ac 3208118 150302932 System.String
00265090 363 247694656 Free
Total 9035539 objects
Таким образом, есть свободная память, но она фрагментирована, и все ее части меньше 16 МБ (выделенный сегмент по умолчанию).
Массив байтов, int и int64 хранится в Lucene Cache. Кэш активирован из-за запроса, который использует сортировку. Реализация кэша Lucene основана на WeakReferenceHashMap и поэтому должна быть очищена сборщиком мусора в случае нехватки памяти.
Команда Heapstat
0:034> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 1643476 2689484 526084512 196389976
Free space: Percentage
Heap0 12 12 170262384 77432248SOH: 32% LOH: 39%
Дамп исключения из файла журнала выглядит так:
Quartz.Core.ErrorLogger - Job (DEFAULT.precalculate-similar-index threw an exception.
Quartz.SchedulerException: Job threw an unhandled exception. ---> System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at Lucene.Net.Search.FieldCacheImpl.LongCache.CreateValue(IndexReader reader, Entry entryKey) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 685
at Lucene.Net.Search.FieldCacheImpl.Cache.Get(IndexReader reader, Entry key) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 240
at Lucene.Net.Search.FieldCacheImpl.GetLongs(IndexReader reader, String field, LongParser parser) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 639
at Lucene.Net.Search.FieldCacheImpl.LongCache.CreateValue(IndexReader reader, Entry entryKey) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 667
at Lucene.Net.Search.FieldCacheImpl.Cache.Get(IndexReader reader, Entry key) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 240
at Lucene.Net.Search.FieldCacheImpl.GetLongs(IndexReader reader, String field, LongParser parser) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldCacheImpl.cs:line 639
at Lucene.Net.Search.FieldComparator.LongComparator.SetNextReader(IndexReader reader, Int32 docBase) in C:\Dev\Lucene.Net_2_9_2\src\Lucene.Net\Search\FieldComparator.cs:line 481
Единственная идея, о которой я узнал, состоит в том, что исключение вызвано фрагментацией памяти. К сожалению, нет ответа на вопрос, почему память не сжимается.
Мы не прикрепляем никаких объектов, и кажется, что Lucene тоже не делает этого, хотя команда! Gcroot возвращает для некоторых объектов следующий результат:
DOMAIN(0025D260):HANDLE(Pinned):1f13ec:Root: 02393250(System.Object[]) - from !gcroot
ESP:16f2e4: sizeof(02393250) = 123436600 ( 0x75b7e38) bytes (System.Object[]) - size of the pinned arrays of objects
Система: Windows Server 2008 R2, 32-битная
Всего передано байтов: ~ 950 МБ
Всего зарезервированных байтов: ~ 1 666 МБ (цифры взяты из системного монитора)
Средство поиска индекса и, следовательно, соответствующее средство чтения индекса регулярно закрываются после выполнения короткого пакета. После этого планируется новая партия, и работа продолжается. ООМ появляется через пару часов работы. Также возникает исключение, и служба продолжает работать.