4G / LTE上的Android HttpClient OOM(HTC Thunderbolt)

时间:2011-03-18 21:28:33

标签: android httpclient 4g htc-android htc-thunderbolt

当我在Verizon的4G / LTE上使用我的应用程序时,我收到了一些崩溃用户的报告。

查看堆栈跟踪,看起来Android的HttpClient.execute()实现正在抛出一个OOM。这仅发生在4G / LTE设备上,特别是HTC Thunderbolt,并且仅在4G / LTE上。 WiFi,3G,UMTS都可以。也适用于Sprint的WiMax 4G工作正常。

两个问题:

  • 关于Android开发者关注此问题的最佳方式是什么?比http://code.google.com/p/android/issues报告更好的选择吗?

  • 关于我如何解决这个问题的任何想法?我自己没有4G设备,我无法在模拟器中实现这一点,所以我需要在这里做一些有根据的猜测。我可以尝试在我的代码中捕获OOM并尝试清理并强制GC,但我不确定这是不是一个好主意。评论或其他建议?

这是我的代码正在做的事情:

    HttpParams params = this.getHttpParams(); // returns params
    ClientConnectionManager cm = new ThreadSafeClientConnManager(params, this.getHttpSchemeRegistry() );
    DefaultHttpClient httpClient = new DefaultHttpClient( cm, params );

    HttpResponse response = null;
    request = new HttpGet( url );

    try {

        response = httpClient.execute(request); // <-- OOM on 4G/LTE. OK otherwise
        int statusCode = response.getStatusLine().getStatusCode();
        Log.i("fetcher", "execute returned, http status " + statusCode );

    ...

这是崩溃的堆栈跟踪:

  

E / dalvikvm-heap(11639):内存不足   在2055696字节分配。   I / dalvikvm(11639):“Thread-16”prio = 5   tid = 9 RUNNABLE I / dalvikvm(11639):|   group =“main”sCount = 0 dsCount = 0 s = N.   obj = 0x48563070 self = 0x3c4340   I / dalvikvm(11639):| sysTid = 11682   nice = 0 sched = 0/0 cgrp =默认值   handle = 3948760 I / dalvikvm(11639):|   schedstat =(208709711 74005130 214)

     

I / dalvikvm(11639):at   org.apache.http.impl.io.AbstractSessionInputBuffer.init(AbstractSessionInputBuffer.java:~79)   I / dalvikvm(11639):at   org.apache.http.impl.io.SocketInputBuffer。(SocketInputBuffer.java:93)   I / dalvikvm(11639):at   org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83)   I / dalvikvm(11639):at   org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170)   I / dalvikvm(11639):at   org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106)   I / dalvikvm(11639):at   org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129)   I / dalvikvm(11639):at   org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:173)   I / dalvikvm(11639):at   org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)   I / dalvikvm(11639):at   org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)   I / dalvikvm(11639):at   org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348)   I / dalvikvm(11639):at   org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)   I / dalvikvm(11639):at   org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)   I / dalvikvm(11639):at   org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)   I / dalvikvm(11639):at   com.myapplication.Fetcher.trySourceFetch(Fetcher.java:205)   I / dalvikvm(11639):at   com.myapplication.Fetcher.run(Fetcher.java:298)   I / dalvikvm(11639):at   java.lang.Thread.run(Thread.java:1102)   I / dalvikvm(11639):E / dalvikvm(11639):   内存不足:堆大小= 24171KB,   分配= 23142KB,位图大小= 59KB,   限制= 21884KB E / dalvikvm(11639):额外   info:Footprint = 24327KB,允许   足迹= 24519KB,修剪= 348KB   W / dalvikvm(11639):threadid = 9:thread   离开未被捕获的例外   (组= 0x40025b38)

3 个答案:

答案 0 :(得分:26)

  

查看堆栈跟踪,看起来Android的HttpClient.execute()实现正在抛出一个OOM。

您在此问题上的堆栈跟踪未指示。当然,你没有在这个问题上提供整个堆栈跟踪。

  

关于Android开发者关注此问题的最佳方式是什么?比http://code.google.com/p/android/issues报告更好的选择吗?

这是一个纯粹的Android bug的可能性很小,但不是零。

以下是其他一些可能性,没有特别的顺序:

  1. execute()本身没有问题,但是你只是内存耗尽,而你遇到的堆栈跟踪只是证明execute()正在给你的堆压力。

  2. 问题在于HTC为Android制作了一些针对Thunderbolt的修改,可能只在LTE网络上生效。

  3. 这个问题在某种程度上是由Verizon LTE网络本身造成的(例如,他们的某些代理发回了导致HttpClient陷入困境的搞砸信息)。

  4.   

    关于如何解决这个问题的任何想法?

    首先,我使用现有的工具(例如,转储HPROF并使用Eclipse MAT进行检查)来​​确认您通常没有内存泄漏,因为Thunderbolt / LTE组合似乎正在绊倒。

    接下来,我建议您想出一些方法来始终如一地重现错误。这可能是您现有的应用程序,需要遵循一系列步骤,或者它可以是专用应用程序(例如,记录触发OOM的URL,然后创建一个只执行HttpClient请求的小应用程序)。我希望DeviceAnywhere有一个Thunderbolt,但它看起来不像。我会放一些触角,看看我是否能在这方面得到一些帮助。

    就解决这个问题而言,作为权宜之计,您可以通过android.os.Build数据检测到您在Thunderbolt上运行,也许您通过ConnectivityManager检测到您在LTE上运行(我是我猜测LTE会列为WiMAX,但这只是猜测,并警告用户该组合的问题。

    除此之外,您可以尝试稍微改变您的HttpClient使用情况,看看它是否有效果,例如:

    • 如果您只支持API级别8或更高级别,则可以AndroidHttpClient作为免费替换
    • 禁用多线程访问(一般情况下或特定于Thunderbolt)并删除ThreadSafeClientConnManager

    对不起,我在这里没有“魔弹”答案。


    <强>更新

    现在我已经拥有完整的堆栈跟踪,查看源代码是......有点亮。

    问题似乎是:

    HttpConnectionParams.getSocketBufferSize(params);
    

    返回触发OOM的2MB左右的值。这是一个非常大的缓冲区,特别是对于Dalvik GC引擎而言,它可能会变得支离破碎(是的,再次出现这个词)。

    params这里是HttpParams。您似乎是通过getHttpParams()自己创建的。例如,AndroidHttpClient将其设置为8192:

    HttpConnectionParams.setSocketBufferSize(params, 8192);
    

    如果您自己设置套接字缓冲区大小,请尝试减少它。如果没有,请尝试将其设置为8192并查看是否有帮助。

答案 1 :(得分:4)

这是修复:https://review.source.android.com/22852

同时,URLConnection是免疫的。它只有HttpClient才有这个问题。

如果您是想要测试此类故障的开发人员,可以使用“adb shell setprop”设置,例如“net.tcp.buffersize.wifi”,以便最大读/写套接字缓冲区大小为当你的设备在wifi上时很大。像下面这样的东西将是一个真正的压力测试:

adb shell setprop net.tcp.buffersize.wifi 4096,80999999,80999999,4096,80999999,80999999

这种配置更改可以解决HttpClient错误。我不知道Thunderbolt上的确切值是什么,但有设备的人可能会发现使用“adb shell getprop | grep buffersize”。

答案 2 :(得分:3)

也许这会有所帮助:

// Set the timeout in milliseconds until a connection is established.
int timeoutConnection = 5000;

// Set the default socket timeout (SO_TIMEOUT) 
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 4000;

// set timeout parameters for HttpClient 
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
HttpConnectionParams.setSocketBufferSize(httpParameters, 8192);//setting setSocketBufferSize

DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.setParams(httpParameters);