Why Excessive File Descriptors Crash Android OpenGL on Adreno GPUs (and How to Fix It)
A detailed case study reveals that leaking file descriptors in Android, especially from SoundPool, exhaust the 1024‑FD limit on Adreno GPUs, causing OpenGL swap buffer failures and a variety of crashes, and shows how proper FD management resolves the issue.
Background
In *nix systems, many resources are represented as file descriptors (FD), such as regular files, sockets, and standard streams. Each process has a limit on the number of FDs it can open; on Android this limit is 1024.
Case Analysis
During Android development we encountered strange crashes that were not captured by sigaction, libcorkscrew, or third‑party crash reporters. Bugly’s Java call stack showed common information, and screenshots indicated a link to OpenGL.
Log analysis showed that almost all crashes occurred on devices with Adreno GPUs and were accompanied by requestBuffer failed messages.
Testing on various devices confirmed that only Adreno‑based devices (Xiaomi 3, HTC M8, Huawei P7, etc.) exhibited the issue, while devices with Tegra or Mali GPUs did not.
Further logs revealed the error:
E/MemoryHeapBase(18703): error creating ashmem region: Too many open filesThis indicated that the process had opened too many files. We printed all open file handles, which showed the number continuously increasing and approaching the FD limit, eventually causing eglSwapBuffers to fail.
If a program opens too many files, OpenGL swap buffer fails.
We hypothesized that the Adreno driver requests a new FD during swap buffer; when the FD limit is reached, the driver cannot obtain the needed FD, leading to swap failures and subsequent crashes or hangs.
Solution
Investigation revealed that using SoundPool to play many sound effects leaked file descriptors. The original code was:
private SoundPool m_soundPool;
public int loadSound(String path) {
int soundID = m_soundPool.load(getAssets().openFd(path), 0);
return soundID;
}
public void unloadSound(int soundID) {
m_soundPool.unload(soundID);
}Although we called unload, the AssetFileDescriptor opened by openFd was never closed, causing FD leaks. Forcing System.gc() helped on some devices (e.g., Xiaomi 3) but not on others (e.g., HTC M8).
We rewrote the code to keep a map of AssetFileDescriptor objects and close them explicitly when unloading:
private SoundPool m_soundPool;
private HashMap<Integer, AssetFileDescriptor> m_soundFdMap = new HashMap<>();
public int loadSound(String path) {
AssetFileDescriptor fd = getAssets().openFd(path);
int soundID = m_soundPool.load(fd, 0);
m_soundFdMap.put(soundID, fd);
return soundID;
}
public void unloadSound(int soundID) {
m_soundPool.unload(soundID);
m_soundFdMap.get(soundID).close();
}After this change, no further FD leaks were observed and the crashes disappeared across all tested devices.
Summary
The root cause was excessive file descriptor usage, particularly from SoundPool leaks, which exhausted Android’s 1024‑FD limit on devices with Adreno GPUs, causing OpenGL swap buffer failures and a variety of crash symptoms. Proper FD management, akin to memory leak handling, is essential to prevent such instability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
