在大型 body 上改造 OOM

Retrofit OOM on large body

我正在使用改装发送相当大的文件,并且偶尔会遇到 OOM。方法:

@POST("/psm-service/model/{model}/video")
void sendVideo(@Header("X-Facebook-Access-Token") String token,
               @Header("X-FB-Id") long fbId,
               @Header("Content-Disposition") String contentDisposition,
               @Path("model") long modelId,
               @Body TypedOutput video,
               Callback<Response> callback);

和服务:

RestAdapter adapter = new RestAdapter.Builder()
            .setClient(new OkClient(Certification.getClient(this)))
            .setEndpoint(Api.ENDPOINT)
            .setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.BASIC : RestAdapter.LogLevel.NONE)
            .build();
    service = adapter.create(Api.ApiService.class);

客户端是一个带有自定义 SSL 的 OkHttpClient。我的 body 是一个使用 android 内容 URI 并设置长度、mimetype 和文件名的 TypedOutput。

@Override
public void writeTo(OutputStream out) throws IOException {
    Utils.copyStream(getContentResolver().openInputStream(uri), out);
}

public static void copyStream(InputStream input, OutputStream output)
        throws IOException
{
    byte[] buffer = new byte[1024]; // Adjust if you want
    int bytesRead;
    while ((bytesRead = input.read(buffer)) != -1)
    {
        output.write(buffer, 0, bytesRead);
    }
}

据我了解,这应该以 1KB 的块发送我的 50 多 MB 文件。由于服务器限制,我必须传递文件大小。我试过完全关闭日志记录,但这没有用。

在其中一个较大的文件中,OOM 被抛入此方法。

java.lang.OutOfMemoryError: Failed to allocate a 120252388 byte allocation with 16777216 free bytes and 81MB until OOM
        at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:95)
        at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:146)
        at java.lang.StringBuffer.append(StringBuffer.java:219)
        at com.splunk.mint.network.io.OutputStreamMonitor.updateBody(OutputStreamMonitor.java:67)
        at com.splunk.mint.network.io.OutputStreamMonitor.write(OutputStreamMonitor.java:58)
        at okio.Okio.write(Okio.java:78)
        at okio.AsyncTimeout.write(AsyncTimeout.java:155)
        at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:133)
        at okio.RealBufferedSink.write(RealBufferedSink.java:45)
        at com.squareup.okhttp.internal.http.HttpConnection$FixedLengthSink.write(HttpConnection.java:300)
        at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:133)
        at okio.RealBufferedSink.write(RealBufferedSink.java:148)
        at lt.segfoltas.common.util.Utils.copyStream(Utils.java:51)
        at lt.segfoltas.psm_casting.net.FormUploaderService$TypedStream.writeTo(FormUploaderService.java:565)
        at retrofit.client.OkClient.writeTo(OkClient.java:88)
        at com.squareup.okhttp.Call.getResponse(Call.java:237)
        at com.squareup.okhttp.Call.execute(Call.java:84)
        at retrofit.client.OkClient.execute(OkClient.java:53)
        at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
        at retrofit.RestAdapter$RestHandler.access0(RestAdapter.java:220)
        at retrofit.RestAdapter$RestHandler.obtainResponse(RestAdapter.java:278)
        at retrofit.CallbackRunnable.run(CallbackRunnable.java:42)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at retrofit.Platform$Android.run(Platform.java:142)
        at java.lang.Thread.run(Thread.java:818)

编辑:

我对堆栈跟踪中的 bugsense 方法表示怀疑,所以我注释掉了 Mint.initAndStartSession,现在我的堆甚至保持在 48MB,而之前是 200MB。虽然这解决了我的问题,但我想继续使用 bugsense。

显然 Splunk Mint 有 issues 和 OkHttp。禁用网络监控可解决此问题。