<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2" -->

---
title: How I Found a Deadlock Inside ART That ART Itself...
description: A deep-dive investigation into a whole-VM lock-ordering deadlock in Android's ART runtime (Android 16, ART mainline v371000140+) triggered when attaching a...
canonical: https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: How I Found a Deadlock Inside ART That ART Itself Couldn’t Dump | daily.dev
og:description: A deep-dive investigation into a whole-VM lock-ordering deadlock in Android's ART runtime (Android 16, ART mainline v371000140+) triggered when attaching a...
og:url: https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2
og:image: https://api.daily.dev/og/posts/6nKAWyFh2.png
og:image:alt: How I Found a Deadlock Inside ART That ART Itself Couldn’t Dump
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# How I Found a Deadlock Inside ART That ART Itself Couldn’t Dump

**[ProAndroidDev](https://daily.dev/sources/pand)** · 13 min read · 1 upvotes · 0 comments

## Summary

A deep-dive investigation into a whole-VM lock-ordering deadlock in Android's ART runtime (Android 16, ART mainline v371000140+) triggered when attaching a Java debugger (JDWP) to large apps. The deadlock occurs between the garbage collector's system-weak processing phase and the JDWP commonRef monitor — a classic ABBA lock-ordering inversion that prevents the VM from reaching a safepoint, making standard Java stack dumps impossible. The author demonstrates how to diagnose such hangs using native ptrace thread dumps and per-thread kernel wait-state analysis (wchan) instead of Java dumps. The bug was reproduced on both Samsung Galaxy S22 hardware and a stock Google Play arm64 emulator, bisected to a specific ART mainline regression window (between versionCode 361143214 and 371000140), and filed publicly as Issue #530992434. Workarounds include pinning an older ART mainline version or isolating debug targets with smaller startup footprints.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://proandroiddev.com/how-i-found-a-deadlock-inside-art-that-art-itself-couldnt-dump-ff7b33b0334a>

## Questions this post answers

### Why does attaching a Java debugger to a large debuggable Android app cause the whole process to freeze and ANR on Android 16?

It is caused by a lock-ordering deadlock inside ART between the garbage collector and the JDWP debug layer. The GC thread blocks trying to acquire the libjdwp commonRef monitor while delivering object-freed events, while that monitor is held by a JDWP event thread waiting for the GC to complete — an ABBA lock inversion. It reproduces reliably on ART mainline versionCode 371000140 onward (Android 16), but not on 361143214 and earlier.

_Track ART runtime regressions like this on daily.dev before they blindside a debugging session._

### Why won't a Java stack dump (kill -3 / ANR trace) work when an Android app is completely hung?

A SIGQUIT Java dump requires a safepoint, meaning every thread must reach a consistent stop point, but if the GC is stuck mid system-weak-reference processing the safepoint is unreachable and the dump times out. In that state a native thread dump via ptrace, combined with per-thread kernel wait channels (futex_wait_queue_me, binder_wait_for_work, do_epoll_wait), is the only way to see what each thread is blocked on.

_Developers troubleshooting frozen JVMs can bookmark diagnostic techniques like this on daily.dev._

### What conditions make an Android app reliably trigger the ART JDWP debugger deadlock?

Three pressure signals must overlap during the debugger attach window: a CLASS_PREPARE event storm from tens of thousands of classes, a THREAD_END event storm from hundreds of concurrent threads, and heavy GC pressure from allocation at startup. Small apps never generate enough load on the debug layer to hit the narrow lock-ordering window, so they attach instantly while large apps deadlock within seconds.

_Teams debugging large Android codebases can follow runtime gotchas like this via daily.dev._

## Similar posts on daily.dev

- [Android Reverse Engineering with Frida: From a Silent Hook to ART StringFactory](https://daily.dev/posts/android-reverse-engineering-with-frida-from-a-silent-hook-to-art-stringfactory-acs5bph4f) · Medium · 0 upvotes · 0 comments
- [18% Faster Compiles, 0% Compromises](https://daily.dev/posts/18-faster-compiles-0-compromises-jbenicddz) · Android Developers Blog · 0 upvotes · 0 comments
- [Enabling R8 optimization at scale with AI-assisted debugging](https://daily.dev/posts/enabling-r8-optimization-at-scale-with-ai-assisted-debugging-kovtjstfc) · Grab Tech Blog · 0 upvotes · 0 comments
- [Thread Dumps and Project Loom \(Virtual Threads\)](https://daily.dev/posts/thread-dumps-and-project-loom-virtual-threads--toxvuqohy) · JetBrains · 0 upvotes · 0 comments

---

[View this post on daily.dev](https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"How I Found a Deadlock Inside ART That ART Itself Couldn’t Dump","url":"https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2"},"datePublished":"2026-07-06T17:21:58.240Z","dateModified":"2026-09-13T20:02:54.745Z","description":"A deep-dive investigation into a whole-VM lock-ordering deadlock in Android's ART runtime (Android 16, ART mainline v371000140+) triggered when attaching a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/99daa2009167a3b30d88d1a96248d079?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/99daa2009167a3b30d88d1a96248d079?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"ProAndroidDev","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"ProAndroidDev","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/656c1eb1f1ef4f91936e86fc16545114","url":"https://daily.dev/sources/pand"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"","timeRequired":"PT13M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"ProAndroidDev","item":"https://daily.dev/sources/pand"},{"@type":"ListItem","position":3,"name":"How I Found a Deadlock Inside ART That ART Itself Couldn’t Dump"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/how-i-found-a-deadlock-inside-art-that-art-itself-couldn-t-dump-6nkawyfh2#faq","mainEntity":[{"@type":"Question","name":"Why does attaching a Java debugger to a large debuggable Android app cause the whole process to freeze and ANR on Android 16?","acceptedAnswer":{"@type":"Answer","text":"It is caused by a lock-ordering deadlock inside ART between the garbage collector and the JDWP debug layer. The GC thread blocks trying to acquire the libjdwp commonRef monitor while delivering object-freed events, while that monitor is held by a JDWP event thread waiting for the GC to complete — an ABBA lock inversion. It reproduces reliably on ART mainline versionCode 371000140 onward (Android 16), but not on 361143214 and earlier. Track ART runtime regressions like this on daily.dev before they blindside a debugging session."}},{"@type":"Question","name":"Why won't a Java stack dump (kill -3 / ANR trace) work when an Android app is completely hung?","acceptedAnswer":{"@type":"Answer","text":"A SIGQUIT Java dump requires a safepoint, meaning every thread must reach a consistent stop point, but if the GC is stuck mid system-weak-reference processing the safepoint is unreachable and the dump times out. In that state a native thread dump via ptrace, combined with per-thread kernel wait channels (futex_wait_queue_me, binder_wait_for_work, do_epoll_wait), is the only way to see what each thread is blocked on. Developers troubleshooting frozen JVMs can bookmark diagnostic techniques like this on daily.dev."}},{"@type":"Question","name":"What conditions make an Android app reliably trigger the ART JDWP debugger deadlock?","acceptedAnswer":{"@type":"Answer","text":"Three pressure signals must overlap during the debugger attach window: a CLASS_PREPARE event storm from tens of thousands of classes, a THREAD_END event storm from hundreds of concurrent threads, and heavy GC pressure from allocation at startup. Small apps never generate enough load on the debug layer to hit the narrow lock-ordering window, so they attach instantly while large apps deadlock within seconds. Teams debugging large Android codebases can follow runtime gotchas like this via daily.dev."}}]}
```

