Skip to content

fix: Close HardwareBuffer Java ref#3904

Merged
mrousavy merged 8 commits into
mainfrom
fix/close-hardware-buffer
May 22, 2026
Merged

fix: Close HardwareBuffer Java ref#3904
mrousavy merged 8 commits into
mainfrom
fix/close-hardware-buffer

Conversation

@mrousavy
Copy link
Copy Markdown
Owner

@mrousavy mrousavy commented May 20, 2026

Previously, we did not close the HardwareBuffer from Java/Kotlin.

We only passed it to C++ (NativeBufferHelper or Nitro's ArrayBuffer) which internally managed it's ref via AHardwareBuffer_acquire(..) and AHardwareBuffer_release(..) - but the Java HardwareBuffer was never closed.
I thought close() force deletes the underlying native buffer memory, but apparently my understanding is wrong - it simply releases the Java reference to the native buffer memory (kinda like doing a AHardwareBuffer_release(..) call).

Also I thought I'd be closing memory CameraX/ImageReader still needs (eg I thought it was pooled HardwareBuffer objects), but it seems like image.hardwareBuffer isn't a cached property: it was calling Image.getHardwareBuffer(), which created a new HardwareBuffer on each call - so we are apparently not closing any pooled HardwareBuffer objects but instead just a ref-counted Java handle. Seemed fine to close.

So not calling close() on our HardwareBuffer (which we often got from Image.getHardwareBuffer()) actually caused issues - it only fully released it after the Java GC deleted the HardwareBuffer Java object.

I wouldn't call it a leak since it did delete them, but it deleted them only after GC runs, even though we could've deleted them sooner.

For example, see the change in ImageProxy+getNativeBuffer.kt - we don't need the hardwareBuffer Java reference after this method returns - we will only have it in C++. It's fine to close this reference here now, and have C++ do the ref counting from there on.

Same for ImageProxy+getPixelBuffer.kt. After Nitro's ArrayBuffer.wrap(hardwareBuffer) consumed the HardwareBuffer, we no longer need the Java reference and can actually close it.


Summary by cubic

Close Java HardwareBuffer references immediately after handing them to native code to free memory sooner and avoid waiting for GC. This makes buffer lifetime deterministic and reduces transient memory use.

  • Bug Fixes
    • getNativeBuffer: use try/finally and call hardwareBuffer.close() after creating NativeBuffer.
    • getPixelBuffer: close hardwareBuffer after ArrayBuffer.wrap(hardwareBuffer) and when checking hasPixelBuffer.
    • Native ref counting remains unchanged; only the Java handle is closed.

Written for commit 95f5f36. Summary will update on new commits. Review in cubic

@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-native-vision-camera-docs Ready Ready Preview, Comment May 22, 2026 12:31pm

Request Review

@mrousavy mrousavy merged commit f658097 into main May 22, 2026
9 of 13 checks passed
@mrousavy mrousavy deleted the fix/close-hardware-buffer branch May 22, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant