草庐IT

android - ByteBuffer 不释放内存

coder 2023-11-22 原文

在 Android 上,一个直接的 ByteBuffer 似乎永远不会释放它的内存,即使在调用 System.gc() 时也是如此。

例子:做

Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));
ByteBuffer buffer = allocateDirect(LARGE_NUMBER);
buffer=null;
System.gc();
Log.v("?", Long.toString(Debug.getNativeHeapAllocatedSize()));

在日志中给出两个数字,第二个数字至少比第一个大 LARGE_NUMBER。

如何消除这种泄漏?


添加:

根据 Gregory 的建议,在 C++ 端处理 alloc/free,然后我定义了

JNIEXPORT jobject JNICALL Java_com_foo_bar_allocNative(JNIEnv* env, jlong size)
    {
    void* buffer = malloc(size);
    jobject directBuffer = env->NewDirectByteBuffer(buffer, size);
    jobject globalRef = env->NewGlobalRef(directBuffer);
    return globalRef;
    }

JNIEXPORT void JNICALL Java_com_foo_bar_freeNative(JNIEnv* env, jobject globalRef)
    {
    void *buffer = env->GetDirectBufferAddress(globalRef);
    free(buffer);
    env->DeleteGlobalRef(globalRef);
    }

然后我在 JAVA 端获取我的 ByteBuffer

ByteBuffer myBuf = allocNative(LARGE_NUMBER);

并用

释放它
freeNative(myBuf);

不幸的是,虽然它确实分配得很好,但它 a) 仍然保持根据 Debug.getNativeHeapAllocatedSize() 分配的内存和 b) 导致错误

W/dalvikvm(26733): JNI: DeleteGlobalRef(0x462b05a0) failed to find entry (valid=1)

我现在彻底糊涂了,我想我至少了解了 C++ 方面的事情……为什么 free() 不返回内存?我对 DeleteGlobalRef() 做错了什么?

最佳答案

没有泄漏。

ByteBuffer.allocateDirect() 从 native 堆/自由存储(想想 malloc())分配内存,然后将其包装到 ByteBuffer 实例。

ByteBuffer 实例被垃圾收集时, native 内存将被回收(否则您将泄漏 native 内存)。

您正在调用 System.gc(),希望 native 内存立即被回收。但是,调用 System.gc() 只是一个请求,它解释了为什么您的第二条日志语句没有告诉您内存已被释放:这是因为它还没有!

在您的情况下,Java 堆中显然有足够的空闲内存,垃圾收集器决定什么都不做:因此,尚未收集无法访问的 ByteBuffer 实例,它们的终结器未运行并且 native 内存未释放。

此外,请记住 bug在 JVM 中(虽然不确定它如何应用于 Dalvik),其中直接缓冲区的大量分配导致不可恢复的 OutOfMemoryError


您评论过从 JNI 进行控制。这实际上是可能的,您可以执行以下操作:

  1. 发布一个 native ByteBuffer allocateNative(long size) 入口点:

    • 调用 void* buffer = malloc(size) 分配 native 内存
    • 通过调用 (*env)->NewDirectByteBuffer(env, buffer, size);
    • 将新分配的数组包装到 ByteBuffer 实例中
    • 使用(*env)->NewGlobalRef(env, directBuffer);ByteBuffer 本地引用转换为全局引用
  2. 发布一个 native void disposeNative(ByteBuffer buffer) 入口点:

    • *(env)->GetDirectBufferAddress(env, directBuffer); 返回的直接缓冲区地址上调用 free();
    • 使用(*env)->DeleteGlobalRef(env, directBuffer);删除全局引用

一旦您在缓冲区上调用了 disposeNative,您就不应再使用该引用,因此它很容易出错。重新考虑您是否真的需要对分配模式进行这种显式控制。


忘记我所说的全局引用。实际上,全局引用是一种在 native 代码中存储引用的方法(如在全局变量中),以便进一步调用 JNI 方法可以使用该引用。所以你会有例如:

  • 从 Java 调用本地方法 foo(),它从本地引用(通过从 native 端创建对象获得)创建全局引用并将其存储在本地全局变量中(作为工作对象)
  • 再次从 Java 返回,调用本地方法 bar() 获取 foo() 存储的 jobject 和进一步处理它
  • 最后,仍然来自 Java,对 native baz() 的最后一次调用删除了全局引用

抱歉造成混淆。

关于android - ByteBuffer 不释放内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5060307/

有关android - ByteBuffer 不释放内存的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  3. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  4. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  5. ruby-on-rails - HTTParty 的内存问题和下载大文件 - 2

    这会导致Ruby出现内存问题吗?我知道如果大小超过10KB,Open-URI会写入TempFile。但是HTTParty会在写入TempFile之前尝试将整个PDF保存到内存吗?src=Tempfile.new("file.pdf")src.binmodesrc.writeHTTParty.get("large_file.pdf").parsed_response 最佳答案 您可以使用Net::HTTP。参见thedocumentation(特别是标题为“流媒体响应机构”的部分)。这是文档中的示例:uri=URI('http://e

  6. ruby-on-rails - 内存中具有相同 ID 的更多对象? - 2

    在部署在heroku上的Rails应用程序(v:3.1)中,我在内存中获得了更多具有相同ID的对象。我的heroku控制台日志:>>Project.find_all_by_id(92).size=>2>>ActiveRecord::Base.connection.execute('select*fromprojectswhereid=92').to_a.size=>1这怎么可能?可能是什么问题? 最佳答案 解决方案根据您的SQL查询,您的数据库中显然没有重复条目。也许您的类项目中的size或length方法已被覆盖。我试过find_

  7. ruby - rails 3.0.7 内存泄漏 - 2

    我的两个不同的Rails应用程序的内存有一些奇怪的问题。这两个应用程序都使用rails3.0.7。每个Controller请求分配20-30-50MB的内存。在生产模式下,这个数量减少到5-10。但这是同样的事情。这是两个应用程序使用的gem列表:gem'pg'gem'haml'gem'sass'gem'devise'gem'simple_form'gem'state_machine'gem"globalize3","0.1.0.beta"gem"easy_globalize3_accessors"gem'paperclip'gem'andand'关闭所有这些gem不会给我任何结果。我

  8. ruby - 如何强制 Ruby 释放内存给操作系统 - 2

    正如标题,我有一个处理大量数据的ruby​​程序。该程序占用了所有内存,其中调用了系统命令hostname,并且发生错误无法分配内存-主机名我试过GC.start但它不起作用。那么如何强制ruby释放未使用的内存呢?OK,这是别人的测试代码,最后报错是big_var被回收了。但是内存仍然没有释放。require"weakref"defreportputs"#{param}:\t\tMemory"+`psax-opid,rss|grep-E"^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1].to_s+'KB'endbig_var=""#big

  9. ruby - 如何在 Ruby 中从内存中 HTTP 发布流数据? - 2

    我想上传我在运行时用Ruby生成的数据,就像从block中提供上传数据一样。我找到的所有示例仅展示了如何流式传输必须在请求之前位于磁盘上的文件,但我不想缓冲该文件。除了滚动我自己的套接字连接之外,最好的解决方案是什么?这是一个伪代码示例:post_stream('127.0.0.1','/stream/')do|body|generate_xmldo|segment|body 最佳答案 有效的代码。require'thread'require'net/http'require'base64'require'openssl'class

  10. ruby-on-rails - 如何仅修改内存中的 zip 文件? - 2

    我有一个Ruby应用程序,我需要修改现有的zip文件。我想在内存中构建zip文件并流回字节,而无需将文件写入文件系统。如果我最终在Heroku上托管它,我认为我无法写入文件系统。有谁知道这样做的方法吗?我看了Zip::ZipFile但看起来它总是想写入文件系统。我想“基于java实现”我将能够只获取压缩文件的字节,这可以在java中完成,但我看不到这样做的方法。编辑:我要问的与此基本相同,但针对Ruby而不是Python:Functiontocreatein-memoryzipfileandreturnashttpresponse 最佳答案

随机推荐