Created
February 23, 2026 06:01
-
-
Save nickname55/889d04d98720dd44fbd01a42603ce8ac to your computer and use it in GitHub Desktop.
JVM SIGSEGV crash: G1GC + Lucene mmap + tmpfs race condition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # JVM SIGSEGV Crash: G1GC + Lucene mmap + tmpfs Race Condition | |
| ## Симптомы | |
| - JVM крашится с `SIGSEGV (0xb)` при интенсивной нагрузке (реиндексация Jira, Lucene indexing) | |
| - Problematic frames: | |
| - `G1CMTask::make_reference_grey()+0x11b` | |
| - `IndexSetIterator::advance_and_next()+0x11b` | |
| - Множество потоков крашатся одновременно (`[thread XXXX also had an error]`) | |
| - Происходит **только на tmpfs (ramdisk)**, на обычном SSD не воспроизводится | |
| - Чем больше CPU ядер — тем чаще крашится | |
| ## Окружение | |
| - Jira 9.3.0 в Docker | |
| - OpenJDK 11 (Temurin 11.0.24 и 11.0.26 — обе падают) | |
| - G1GC (default) | |
| - Данные на tmpfs (/mnt/ramdisk) | |
| - Linux 6.14.0-37-generic, 24 ядра, 125 GB RAM | |
| ## Причина | |
| Race condition между Lucene и G1 Garbage Collector: | |
| 1. **Lucene** использует memory-mapped files (`MappedByteBuffer`) для записи/чтения индексов | |
| 2. **G1GC** параллельно сканирует память через `G1CMTask::make_reference_grey()` — обход ссылок в concurrent marking phase | |
| 3. Lucene создаёт/удаляет mmap-сегменты из множества потоков одновременно | |
| 4. GC попадает на страницу памяти, которую Lucene **только что размапил** (unmap) → SIGSEGV | |
| На tmpfs это проявляется чаще потому что: | |
| - I/O на tmpfs в 50-100x быстрее SSD | |
| - mmap/munmap операции завершаются практически мгновенно | |
| - Окно гонки (race window) значительно шире при высоком параллелизме | |
| - На SSD I/O медленнее → GC успевает увидеть актуальное состояние маппинга | |
| ## Почему больше ядер = больше крашей | |
| - При 15 ядрах: 15 потоков Lucene + GC потоки = десятки конкурентных mmap операций | |
| - При 4 ядрах: значительно меньше конкурентных mmap операций, GC успевает | |
| ## Workarounds | |
| ### 1. Ограничить CPU (работает) | |
| ```yaml | |
| # docker-compose.yml | |
| services: | |
| jira: | |
| cpus: 4 # ограничить до 4 ядер | |
| ``` | |
| Снижает параллелизм → снижает вероятность race condition. | |
| ### 2. Вернуть данные на SSD | |
| Оставить MySQL на tmpfs (основной bottleneck для чтения), а Jira home (индексы Lucene) на SSD. | |
| ### 3. Отключить Compressed Oops (НЕ помогает) | |
| ``` | |
| -XX:-UseCompressedOops | |
| ``` | |
| Пробовали — не помогает, баг не в compressed oops. | |
| ### 4. Сменить GC на ZGC (не проверено) | |
| ``` | |
| -XX:+UnlockExperimentalVMOptions -XX:+UseZGC | |
| ``` | |
| ZGC использует другой механизм обхода ссылок, теоретически может не крашиться. | |
| ### 5. Обновить JDK до 19+ (несовместимо с Jira 9.3) | |
| В JDK 19+ Lucene использует `MemorySegment` API вместо `MappedByteBuffer`. | |
| Но Jira 9.3.0 официально поддерживает только JDK 8 и 11. | |
| ## Не причина | |
| - ❌ Перегрев CPU (температура 31-37°C при лимите 100°C) | |
| - ❌ OOM killer (dmesg чист) | |
| - ❌ Нехватка RAM (90 GB свободно) | |
| - ❌ Версия JDK 11 (11.0.24 и 11.0.26 — обе крашатся) | |
| ## Ссылки | |
| - https://bugs.openjdk.org/browse/JDK-8293114 — JDK-8293114: G1 crash on MappedByteBuffer | |
| - https://issues.apache.org/jira/browse/LUCENE-16091 — Lucene: use MemorySegment on JDK 19+ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment