CN

Overview of Jank and Frame Drops in Android - System Layer

Word count: 2.9kReading time: 17 min
2019/09/05
loading

Technical review · 2026-10-13. These cases and screenshots date from 2019. The review uses relevant Android 8.1/9/16 source, Linux v6.6 documentation, and current Android documentation while preserving the images and the author’s experience. Long slices, CPU numbers, reclaim activity, and input pacing are no longer treated as standalone proof of root cause. No original-device rerun or complete trace replay was performed, and missing conditions are not invented.

The Application Layer article covers app-side problems. Here we organize cases around system services, display, scheduling, and memory. Hardware, kernels, and vendor implementations differ, so retain the actual system version and configuration.

OEM and app teams may use Matrix or their own monitoring. System privileges can expose more evidence, but capture scope still depends on build type, kernel configuration, and enabled sources.

Smoothness is not simply average FPS. An average of 55 FPS over one second on a 60 Hz display does not locate the delayed frames or determine every user’s perception. Examine intended pacing, actual presentation intervals, and input response. Static screens need not submit content on every refresh. The four-part series is:

System-Level Performance Case Studies

These are real cases I collected in 2019. Some surfaced during development, with a chance to fix them before release. Others only appeared after long use and were harder to investigate. Some needed a particular scenario or piece of hardware to show up at all.

Some images establish waiting, composition, or reclaim activity; device-specific implementations and final causes require the original traces, logs, and source. The screenshots do not identify a complete system build, so their explanations are not universal Android behavior.

The images use legacy Systrace; see the Systrace series for background. Prefer Perfetto for current captures. On Android 12 (API 31) and later, when the relevant data is captured, use FrameTimeline to examine expected/actual frames, deadlines, and presentation. Old colors, long slices, and missing slices are starting points for investigation.

1. SurfaceFlinger Main Thread Overhead

SurfaceFlinger coordinates layers and composition. Critical work missing its frame deadline can delay presentation; there is no universal per-call threshold that always drops one frame. HWC, GPU, and display-driver work can be asynchronous, so inspect fences and presentation relationships.

dequeueBuffer may wait for a free buffer, allocation, or synchronization. queueBuffer submits through the producer interface; neither call is universally a Binder call blocked directly inside SurfaceFlinger. Older BufferQueues may cross processes, while Android 16’s BLASTBufferQueue can create a local queue and submit buffers through transactions. Inspect the actual call path.

The first two images show composition and queue timing. The third selects about 28.02 ms and also contains allocateBuffers work. The fourth shows delayed frame production after waiting. These screenshots alone identify neither the responsible layer nor GPU execution time for the whole selection.

2. Under-Screen Ambient Light Sensor Screenshots

The original account concerns one device’s under-display ambient-light implementation using screen content to help estimate ambient light. The Chinese text mixed this up with under-display fingerprint sensing; they are different features. This is not a claim that every under-display sensor takes frequent screenshots.

A vendor feature can interfere with frames if it synchronously occupies composition threads, locks, or GPU resources. The selected 211.867 ms below spans multiple frames and does not show an identifiable capture call or sensor request. Confirm the account through request timing, capture paths, and a controlled comparison with the feature enabled or disabled.

3. HWC Service Execution Overhead

HWC means Hardware Composer. These images show a historical HIDL path through android.hardware.graphics.composer@2.2-service, including PresentDisplay and DRM Commit/AtomicCommit. Long calls can contain driver or fence waits rather than continuous CPU execution.

Composition, submission, and scanout are dependent stages, so delays can propagate toward app queues. A slow HWC call does not automatically mean SurfaceFlinger skips the next composition. In the third image, overlapping CRTC and HWC work does not establish that CRTC is waiting for HWC; inspect wait sites and completion signals. The fourth image is a different process recording, not another view of the same measurement.

4. CRTC Execution Overhead

The crtc_commit track belongs to a display-driver-related kernel thread; its slices are not direct measurements of panel scanout time. The image also shows long dequeueBuffer and setTransactionState activity. Follow Binder, lock, fence, and driver-completion dependencies.

A display-commit delay may block upstream work, but concurrent tracks alone do not establish CRTC as the cause of every wait or guarantee that the next frame will not be composed. Vendor drivers and Android versions change these paths.

5. CPU Scheduling Issues

Analyze thread state, CPU topology, allowed CPUs, scheduling policy/priority, frequency, and frame deadlines together. A CPU number is not a core type. Small-core placement, low frequency, or migration is not inherently a fault; the question is whether critical work finishes in time.

Critical Tasks on Small Cores

Insufficient CPU capacity for critical work can cause missed deadlines. However, the 22.558 ms selection below is a RenderingResponse interval without full topology or thread-state details; it is not directly a single frame’s CPU duration on a small core.

The next image labels RenderThread intervals of about 1.66 ms at 2.4 GHz and 8.6 ms at 0.7 GHz. This motivates inspecting frequency and scheduling, but is not a controlled experiment with identical workload, caches, core type, and waits. The Linux v6.6 EAS documentation explains capacity/energy tradeoffs; check the device kernel before assuming that policy applies.

Low Priority and Slice Delays

A thread on the user-visible critical path may have modest scheduling priority. For a long Runnable interval, inspect competition on its allowed CPUs, policy/priority, cpuset/affinity, and resource limits. Also check dependencies on lower-priority threads. A name or priority value alone does not prove priority inversion.

Preemption by Real-Time Threads

RT here means real-time scheduling, such as SCHED_FIFO/SCHED_RR; it is not an abbreviation for RenderThread, nor a classification applied uniformly to a whole process. Real-time work on an allowed CPU can delay ordinary threads. Verify the policy, preemption, and running intervals.

Android 9 ActivityManagerService contains a sys.use_fifo_ui path, disabled by default, that changes scheduling for TOP_APP UI/RenderThreads. This does not establish that every version leaves it off because startup becomes slower, and is not a recommendation to make all app threads real-time.

Core Binding and Migration

An unsuitable affinity/cpuset or task placement can restrict available capacity. My diagnosis of the CTS case at the time was that the threads were constrained to CPU 7 and contention caused a timeout. The screenshot does not include the affinity mask, complete scheduling policy, or CTS report, so it cannot independently verify that cause.

A single allowed CPU also does not mean the UI thread must wait until RenderThread finishes all its work: ordinary threads can be preempted and time-shared. Inspect allowed CPUs, running competitors, wait duration, and the test deadline.

6. Thermal Throttling

Thermal management can limit CPU/GPU frequency, power, or sustained performance and affect demanding scenes. Heat or downclocking does not inevitably cause jank. Correlate thermal state, frequency limits, and frame timing rather than inferring the cause from surface warmth or one frequency trace.

Android 10 (API 29) added thermal-status APIs, and Android 11 (API 30) added getThermalHeadroom. Headroom is an estimate, not a thermometer; verify support and validity. Reduce sustained load and compare at similar temperatures. Clearing background tasks or rebooting alone does not establish a fix.

7. High Background Activity

Background work can compete with foreground work for CPU, I/O, memory, or shared locks. Process count itself is not a load metric. Inspect active work and its relationship to the critical path instead of treating every background process as a fault.

CPU Saturation

dumpsys cpuinfo reports a time window rather than a single frame’s scheduling. Process values above 100% can reflect accumulated multi-core use, not a percentage of total device capacity; establish the window and denominator. The lowmemorykiller log explicitly includes “no selected tsk,” which is not evidence that those scans killed a process.

Runnable Thread States

Runnable means eligible to run but not currently on a CPU. A long wait on the critical path may miss its deadline, but causes can include allowed CPUs, priority, or quotas rather than every CPU being busy. The pictured thread is UnityGfxDeviceW, not an established app main thread; inspect state details and scheduling records.

Irrelevant Active Processes

Determine relevance from dependencies in the current interaction, not merely a name or background status. The table contains Wall Duration, Average Wall Duration, and Occurrences: aggregated elapsed durations and counts, not CPU utilization percentages. UnityMain and RenderThread may be foreground work. Return to the timeline to see whether these tasks compete on CPUs available to the critical thread.

Memory Reclamation Overhead

Memory pressure can increase reclaim work. kswapd performs background kernel reclaim, while HeapTaskDaemon executes ART heap-maintenance tasks; they are not threads frantically requesting memory. The name alone also does not prove every HeapTaskDaemon interval is GC; inspect GC/trim events.

The image shows activity, not proof that these threads occupy every CPU. Correlate reclaim events, PSI, page faults, running time, and foreground waits before concluding resource contention.

System Lock Contention

The roughly 93.633 ms selection contains several monitor-contention slices involving AMS activityIdle and RecentsAnimation.finishAnimation, supporting the observation of lock contention. Follow the owner thread and nested waits to distinguish computation while holding a lock, preemption, and downstream waiting.

Not every Binder request uses the same AMS/WMS lock, and the full selection is not one lock-hold duration. Delay propagates through requests that depend on the blocked critical section. Check modern lock and animation implementations by version.

8. Excessive Layers

Layers that need traversal, updating, or visible-region computation can add composition-preparation work, but background-app count is not a sufficient measure. The image shows rebuildLayerStacks/visibility work without layer counts or a controlled comparison.

This did not first move into the SurfaceFlinger refresh path in Android P: Android 8.1 SurfaceFlinger already calls rebuildLayerStacks from handleMessageRefresh, as does Android 9. Android 16 uses newer layer/CompositionEngine machinery; the old function name is not a stable interface. Investigate layer lifecycle, leaks, and transaction updates instead of treating routine task clearing as a fix.

9. Uneven Input Reporting

Irregular touch delivery can affect responsiveness, but missing input does not prevent all drawing: animations, flings, timers, and invalidation can continue updates. InputReader/Dispatcher slices are not a one-to-one copy of raw touch reports; batching, coalescing, filtering, and capture loss must be distinguished.

Use this overview to locate an interval, not to prove that the touch device omitted events. Correlate raw timestamps, Reader/Dispatcher/filter stages, app delivery, and presentation to distinguish absent events, delayed dispatch, and delayed app response.

10. LMK Resource Competition

Distinguish the historical LMK from modern userspace lmkd. The AOSP lmkd documentation describes PSI-based pressure detection supported in Android 10 and an updated Android 11 strategy that also considers thrashing, subject to kernel support and device configuration. Active reclaim threads do not mean lmkd itself occupies all CPUs.

  1. CPU: repeated kills and restarts can add work, but a killed cached process is not guaranteed to restart immediately through its “main process.” Match actual kill/start records, PIDs, and timestamps to establish a cycle.
  2. Memory: ART GC, kernel reclaim, and lmkd process killing are different mechanisms. Global memory pressure does not establish simultaneous GC in all processes.
  3. I/O: revisiting reclaimed file pages may require storage reads; anonymous pages may be swapped. With zram, compressed pages primarily reside in RAM, so swap is not always disk I/O. Configured writeback requires separate inspection of storage activity.

The first image is an aggregate list, not a kill/restart count. The second is identical to the HeapTaskDaemon/kswapd image in section 7; it reuses the same historical interval rather than supplying an independent experiment.

11. Low Memory I/O Latency

Memory pressure may add waits through direct reclaim, reloading file pages, swapping, or writeback, but each needs evidence. Startup, GC, JIT, and D state are not interchangeable proof of slow storage caused by low memory. Linux v6.6 vmscan describes reclaim paths; the zram documentation distinguishes compressed memory from optional writeback.

The first image shows NativeAlloc concurrent copying GC, resource loading, and long traversal work. It alone does not identify disk I/O. Assess the following subcases separately.

Main-Thread I/O Waits

Here the SystemUI main thread explicitly shows Uninterruptible Sleep - Block I/O with io_schedule as the wait site, supporting an I/O-related wait in that interval. Identifying a file, device, reclaim path, or lock still needs callstacks and I/O events. D state alone does not always mean disk I/O or prove memory pressure.

Startup Loading and I/O

The first image contains screen-recorder process initialization, bindApplication, OpenDexFilesFromOat, and oat loading. The second includes Application.onCreate, VerifyClass, and JIT. Loading may involve reads, page faults, verification, CPU work, and waits; the entire startup interval is not pure I/O. Also distinguish process initialization, first frame, and user-interactive readiness.

Page Waits During Scrolling

The Launcher main-thread selection is about 57.994 ms and shows Uninterruptible Sleep | WakeKill - Block I/O at wait_on_page_bit_killable. This supports a page-related wait, but does not alone identify a file, establish memory pressure, or diagnose faulty storage. Correlate page faults, reclaim, and block-I/O timing.

12. GPU Composition Overhead

HWC can assign some layers to client composition, which SurfaceFlinger composites through the GPU. This is a normal path. Its impact depends on pixel workload, GPU load, buffers/fences, and display deadlines; GPU composition is not inherently slow. See SurfaceFlinger and HWC.

The images show hasClientComposition alongside FrameMissed. The second selects about 22.291 ms and also annotates an HWC issue. This does not independently establish a GPU cause, nor make the SurfaceFlinger CPU slice or entire selection a GPU execution measurement.

13. KSWAPD CPU Usage

The image shows frequent kswapd0 activity on CPU 7. Its core type requires the SoC topology, and no temperature or throttling evidence appears here. It does not directly prove that big-core reclaim caused thermal throttling. Measure reclaim running time on relevant CPUs, foreground Runnable delay, and sustained memory pressure to assess contention.

14. Uneven SurfaceFlinger Vsync

The legacy VSYNC-sf track represents scheduling signals, not every physical presentation. Uneven spacing merits investigation, but distinguish refresh-rate/display-mode changes, phase adjustments, capture loss, and implementation faults. Colored block widths alone do not implicate a SurfaceFlinger or HWC change.

Modern systems support multiple refresh rates. Correlate modes, target periods, app production, and actual presentation, using FrameTimeline where supported, to distinguish intended policy from missed deadlines.

15. Accessibility Services

Some accessibility features enable input filtering or transformations, with processing in system_server before forwarding. Android 9 AccessibilityManagerService enables InputFilter according to features such as magnification, touch exploration, key filtering, or gesture injection. Merely having an Accessibility service or listening for accessibility events does not imply that same path. Android 16’s AccessibilityInputFilter also handles events according to enabled features.

The image suggests a change in Reader-to-Dispatcher pacing, not that every non-immediate wakeup is a system fault. Correlate event times, filtering/forwarding, and Handler/Binder waits, then compare the specific feature under controlled conditions.

Summary

These were my thoughts on the work in 2019.

Android is a constantly evolving system. Each version solves many performance issues but might introduce new ones. OEMs add substantial custom code, which further impacts performance.

The cases above are just the tip of the iceberg. This is why manufacturers are investing heavily in optimization—from hardware and software to learning from usage and adjusting system policies. Those who neglect quality for pure design often lose their market share.

For more details on what OEMs do, see: What do phone manufacturers actually do when they say they are optimizing Android?

This series continues:

Zhihu Article

Original Chinese article and discussion

About Me && Blog

For my introduction and related articles:

  1. About me
  2. Blog navigation
  3. Android performance optimization: skills and tools
  4. Android performance community: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.

WeChat QR code

CATALOG
  1. 1. System-Level Performance Case Studies
    1. 1.1. 1. SurfaceFlinger Main Thread Overhead
    2. 1.2. 2. Under-Screen Ambient Light Sensor Screenshots
    3. 1.3. 3. HWC Service Execution Overhead
    4. 1.4. 4. CRTC Execution Overhead
    5. 1.5. 5. CPU Scheduling Issues
      1. 1.5.1. Critical Tasks on Small Cores
      2. 1.5.2. Low Priority and Slice Delays
      3. 1.5.3. Preemption by Real-Time Threads
      4. 1.5.4. Core Binding and Migration
    6. 1.6. 6. Thermal Throttling
    7. 1.7. 7. High Background Activity
      1. 1.7.1. CPU Saturation
      2. 1.7.2. Runnable Thread States
      3. 1.7.3. Irrelevant Active Processes
      4. 1.7.4. Memory Reclamation Overhead
      5. 1.7.5. System Lock Contention
    8. 1.8. 8. Excessive Layers
    9. 1.9. 9. Uneven Input Reporting
    10. 1.10. 10. LMK Resource Competition
    11. 1.11. 11. Low Memory I/O Latency
      1. 1.11.1. Main-Thread I/O Waits
      2. 1.11.2. Startup Loading and I/O
      3. 1.11.3. Page Waits During Scrolling
    12. 1.12. 12. GPU Composition Overhead
    13. 1.13. 13. KSWAPD CPU Usage
    14. 1.14. 14. Uneven SurfaceFlinger Vsync
    15. 1.15. 15. Accessibility Services
  2. 2. Summary
  3. 3. Zhihu Article
  4. 4. About Me && Blog