Mobile Development 24 min read

Raphael: An Open‑Source Native Memory Leak Detection Tool for Android

Raphael is a native memory‑leak detection tool developed by Bytedance's video team, open‑sourced to address Android native memory issues through a combination of inline/PLT hooking, efficient stack unwinding, and optimized caching, offering low‑overhead, version‑independent monitoring across diverse native allocation APIs.

Watermelon Video Tech Team
Watermelon Video Tech Team
Watermelon Video Tech Team
Raphael: An Open‑Source Native Memory Leak Detection Tool for Android

Abstract

Raphael is a native memory‑leak detection tool created by the Bytedance video team, widely used across ByteDance apps for native memory‑leak governance, and now open‑sourced. This article analyses its principles, design, and practice.

Background

Memory problems on Android, especially native memory, have long been a performance and stability pain point. While Java heap issues can be handled with mature tools, native memory lacks stable, efficient solutions; existing tools either suffer performance, stability, or compatibility issues.

Current Situation

Several native leak detection tools exist (LeakTracer, MTrace, MemWatch, Valgrind‑memcheck, TCMalloc, LeakSanitizer), but on Android they face compatibility or high integration costs, typically relying on function hooking (malloc/calloc/realloc/memalign/free) and stack unwinding. These tools fall into two hooking categories: hook and LD_PRELOAD.

malloc debug is the Android‑provided native memory debugging tool, but its activation and core functions are limited by Android version, and it introduces severe startup latency and runtime stutter.

LeakTracer

LeakTracer uses LD_PRELOAD to replace allocation functions, but Android restricts LD_PRELOAD and it cannot intercept allocations across multiple .so files, limiting its usefulness.

Common problems of existing tools on Android:

Complex integration : requires wrap scripts, root, setprop, and is version‑dependent.

Compatibility issues : unwind libraries often fail on GNU‑compiled frames.

Performance overhead : malloc debug can degrade performance by more than tenfold.

Our Requirements

The Watermelon Video app contains extensive native code and demands a leak detection tool that is low‑impact, version‑agnostic, stable, and capable of monitoring not only malloc/calloc/realloc/memalign/free but also mmap/mmap64/munmap.

Integration: no Android version dependency, no root, minimal business intrusion.

Stability: must not affect app stability.

Performance: negligible overhead.

Coverage: include mmap/mmap64/munmap.

Raphael Core Design

Raphael’s design focuses on three components: hooking implementation, stack unwinding, and cache management.

Hooking Implementation

Wrap.sh and LD_PRELOAD are unsuitable for Android; malloc hook cannot cover mmap APIs. Raphael adopts inline/PLT hooking via Android‑Inline‑Hook and xHook, later switching to ByteHook for automatic incremental .so handling.

xHook provides stable PLT hooking but incurs overhead when many symbols are hooked; inline hook offers higher efficiency but requires thread suspension for instruction patching.

Stack Unwinding

Native leaks lack explicit reference graphs, so stack unwinding is essential. Raphael evaluates several unwind libraries (libunwind_llvm, libunwind (nongnu), libgcc_s, libudf, etc.) and ultimately adopts libudf for a balance of performance and success rate.

To reduce overhead, Raphael limits unwind frequency by setting memory‑size thresholds, avoiding frequent traces for small allocations that rarely cause OOM.

Unnecessary unwind frames are eliminated by minimizing the call depth between the hook entry and unwind start, using inline hooks or early context construction.

Cache Management

Raphael pre‑allocates a fixed‑size cache, avoiding global locks and dynamic allocations used by malloc‑debug and LeakTracer. It tracks allocations via hashed keys and discards stack aggregation to improve speed.

Monitoring Scope

Beyond malloc family, Raphael also intercepts mmap/mmap64/munmap to capture memory used by threads, WebView, Flutter, hardware acceleration, and graphics buffers, addressing the majority of virtual‑memory consumption.

Special handling is added for thread‑stack release via pthread_exit and for re‑entrancy when malloc internally calls mmap.

Comprehensive Evaluation

Functionality

Raphael supports both malloc family and mmap family APIs, covering virtually all native memory usage scenarios on Android.

Performance

CPU overhead < 3% on low‑end devices with 32‑bit mode and a 1024‑byte threshold.

Memory overhead ~16 MiB virtual memory in 32‑bit mode.

Frame rate impact negligible under the same conditions.

Stability

Open‑source version may freeze on some Android 6 devices; otherwise no major stability issues have been observed.

Governance Practice

Raphael is used internally across many ByteDance apps, helping locate and fix both true leaks and inefficient memory usage in native code, WebView, Camera, and other components, as illustrated by four case studies.

Future Plans

Improvements include more robust inline‑hook solutions, better 32‑bit unwind performance, lock‑free cache management, and extending the monitoring framework to other bounded resources such as JNI reference tables, file descriptors, and Binder.

Conclusion

Raphael demonstrates that a practical, open‑source native memory‑leak detector can be built for Android, offering configurable performance, broad coverage, and sufficient stability for large‑scale production use, while acknowledging the inherent trade‑offs of runtime instrumentation.

AndroidHookingstack unwindingNative Memory LeakRaphael
Watermelon Video Tech Team
Written by

Watermelon Video Tech Team

Technical practice sharing from Watermelon Video

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.