为什么DoubleBuffer.put()分配内存,我该如何避免这种情况发生?

时间:2017-01-02 03:09:00

标签: java android memory nio

在我的Android应用程序中,我在一个线程中有一个循环,它对存储在DoubleBuffer个对象中的数据进行一些处理。令我惊讶的是,put(DoubleBuffer)似乎在分配内存!

这是我应该期待的吗?对我而言,它似乎违背了我认为我对NIO缓冲区的理解。我应该做些什么来避免它?

我能够通过一个简单的例子来重现这一点,这个例子只是在循环中的两个缓冲区之间复制。在运行Android 4.4.2的物理手机上通过Android Studio运行,日志中包含如下行:

D/dalvikvm: GC_FOR_ALLOC freed 2032K, 55% free 6556K/14500K, paused 11ms, total 11ms

如果我注释掉put()的来电,则不会发生这种情况。

这里是我用这个问题重现了这个问题的代码,写在"空活动"模板:

public class MainActivity extends AppCompatActivity {
    private Thread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                DoubleBuffer buffer1 = DoubleBuffer.allocate(5000);
                DoubleBuffer buffer2 = DoubleBuffer.allocate(buffer1.capacity());
                while (!Thread.interrupted()) {
                    // set remaining to capacity so that entire buffer is copied
                    buffer1.clear();
                    buffer2.clear();
                    buffer2.put(buffer1);
                    Thread.yield();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    @Override
    protected void onStop() {
        if (thread != null) {
            thread.interrupt();
            thread = null;
        }
        super.onStop();
    }
}

这里是app/build.gradle的相关部分:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        minSdkVersion 10
        targetSdkVersion 25
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:25.1.0'
}

1 个答案:

答案 0 :(得分:1)

浏览AOSP源代码,这似乎正是发生的事情。 4.4.2DoubleBuffer.put(DoubleBuffer)的相关代码为:

double[] doubles = new double[src.remaining()];
src.get(doubles);
put(doubles);

因此他们需要支付堆分配的费用,这样他们才能获得System.arraycopy()的好处。相同的代码用于DoubleBuffer的{​​{1}}视图,无论是否直接。

据我所知,这是至少早在2.3的代码。自Android 7.0起,它已被替换为:

ByteBuffer

因此,如果您想避免在所有Android版本的紧密循环中分配内存,并且您真的想(或必须)使用缓冲区,则需要自己实现int n = src.remaining(); if (n > remaining()) throw new BufferOverflowException(); for (int i = 0; i < n; i++) put(src.get()); 功能。例如:

put(DoubleBuffer)

或者,如果您从数组支持的缓冲区(while (fromBuffer.hasRemaining()) { toBuffer.put(fromBuffer.get()); } )进行复制:

!fromBuffer.isDirect()

所有非字节缓冲区(toBuffer.put(fromBuffer.array(), fromBuffer.position(), fromBuffer.remaining()); IntBufferLongBufferShortBufferFloatBuffer)的情况相同。