Issue
I'm getting quite a few OutOfMemoryError reports from my users and every single report is from the same Activity, which contains a MapView. I'm thinking that it's an isolated exception with just this one place in my app, and I can't figure out what the problem is. Can anybody give me some pointers as to why this is happening?
I've removed some unneeded code for this question, so if somebody thinks the issue could potentially be in that, I'll post it.
Stack Traces
Stack Trace #1
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:677)
at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444)
at com.google.android.maps.ZoomHelper.beginZoom(ZoomHelper.java:194)
at com.google.android.maps.MapView$2.onScaleBegin(MapView.java:371)
at android.view.ScaleGestureDetector.onTouchEvent(ScaleGestureDetector.java:216)
at com.google.android.maps.MapView.onTouchEvent(MapView.java:646)
at android.view.View.dispatchTouchEvent(View.java:3778)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:920)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:959)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1716)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1124)
at android.app.Activity.dispatchTouchEvent(Activity.java:2125)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1700)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1822)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:5068)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)
Stack Trace #2
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444)
at com.google.android.maps.ZoomHelper.doZoom(ZoomHelper.java:151)
at com.google.android.maps.ZoomHelper.doZoom(ZoomHelper.java:140)
at com.google.android.maps.MapView.doZoom(MapView.java:1478)
at com.google.android.maps.MapView.doZoom(MapView.java:1487)
at com.google.android.maps.MapController.zoomOut(MapController.java:439)
at com.hookedroid.fishingcompanion.GoogleMaps$3.onClick(GoogleMaps.java:133)
at android.view.View.performClick(View.java:2405)
at android.view.View$PerformClick.run(View.java:8813)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Stack Trace #3
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372)
at com.hookedroid.fishingcompanion.maps.CrosshairOverlay.draw(CrosshairOverlay.java:32)
at com.google.android.maps.OverlayBundle.draw(OverlayBundle.java:45)
at com.google.android.maps.MapView.onDraw(MapView.java:494)
at android.view.View.draw(View.java:6742)
at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1913)
at android.view.ViewRoot.draw(ViewRoot.java:1407)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1163)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1727)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4646)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at dalvik.system.NativeStart.main(Native Method)
Activity
public class GoogleMaps extends MapActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.googlemaps_layout);
intent = getIntent();
currentMode = intent.getIntExtra("MAP_MODE", 0);
initControls();
initMembers();
currentOverlayMode = prefs.getInt("map_viewmode", 0);
populateMap();
}
private void initMembers() {
mDbHelper = new FishingCompanionDB(this);
mDbHelper.open();
catchList = new ArrayList<FishEntry>();
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefsEditor = prefs.edit();
}
private void initControls() {
mMaps = (MapView)findViewById(R.id.google_maps);
mMaps.setClickable(true);
mMaps.setLongClickable(true);
mapController = mMaps.getController();
mOverlayModeBtn = (Button)findViewById(R.id.googlemaps_overlay_btn);
mOverlayModeBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (currentOverlayMode < 1)
currentOverlayMode++;
else
currentOverlayMode = 0;
switch (currentOverlayMode) {
case OVERLAY_STREET:
mMaps.setSatellite(false);
mMaps.setStreetView(true);
prefsEditor.putInt("map_viewmode", OVERLAY_STREET);
break;
case OVERLAY_SAT:
mMaps.setStreetView(false);
mMaps.setSatellite(true);
prefsEditor.putInt("map_viewmode", OVERLAY_SAT);
break;
}
prefsEditor.commit();
mMaps.invalidate();
}
});
mZoomInBtn = (Button)findViewById(R.id.googlemaps_btn_zoomin);
mZoomInBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mapController.zoomIn();
}
});
mZoomOutBtn = (Button)findViewById(R.id.googlemaps_btn_zoomout);
mZoomOutBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mapController.zoomOut();
}
});
}
private void populateMap() {
overlays = mMaps.getOverlays();
overlays.clear();
overlays.add(new CrosshairOverlay(this, R.drawable.mapcenter));
mSelectPos = (Button)findViewById(R.id.googlemaps_select_location);
mSelectPos.setVisibility(View.VISIBLE);
mSelectPos.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
GeoPoint centerGp = mMaps.getMapCenter();
double lat = centerGp.getLatitudeE6()/1E6;
double lng = centerGp.getLongitudeE6()/1E6;
Intent i;
if (currentMode == SELECT_POS_WEATHER) {
i = new Intent(GoogleMaps.this, WeatherLookup.class);
i.putExtra("WEATHER_LAT", lat);
i.putExtra("WEATHER_LNG", lng);
}
else {
i = new Intent(GoogleMaps.this, AddLocation.class);
i.putExtra("LOCATION_LAT", lat);
i.putExtra("LOCATION_LNG", lng);
}
i.putExtra("MODE", 1);
startActivity(i);
finish();
}
});
mMaps.invalidate();
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
}
Crosshair Overlay
public class CrosshairOverlay extends Overlay {
private Context mContext;
private int resourceId;
public CrosshairOverlay(Context context, int resId) {
this.mContext = context;
this.resourceId = resId;
}
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
super.draw(canvas, mapView, shadow);
GeoPoint centerGp = mapView.getMapCenter();
Projection projection = mapView.getProjection();
Point centerPoint = projection.toPixels(centerGp, null);
Paint p = new Paint();
Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(), resourceId);
canvas.drawBitmap(bmp, (centerPoint.x - (bmp.getWidth()/2)), (centerPoint.y - (bmp.getHeight()/2)), p);
return true;
}
}
DUMPSYS MEMINFO
** MEMINFO in pid 25493 [com.hookedroid.fishingcompanion] **
native dalvik other total
size: 10036 7495 N/A 17531
allocated: 9955 3965 N/A 13920
free: 80 3530 N/A 3610
(Pss): 3717 1480 6703 11900
(shared dirty): 668 1512 8056 10236
(priv dirty): 3696 804 5024 9524
Objects
Views: 0 ViewRoots: 0
AppContexts: 0 Activities: 0
Assets: 3 AssetManagers: 3
Local Binders: 19 Proxy Binders: 21
Death Recipients: 1
OpenSSL Sockets: 0
SQL
heap: 527 MEMORY_USED: 527
PAGECACHE_OVERFLOW: 62 MALLOC_SIZE: 50
DATABASES
pgsz dbsz Lookaside(b) Dbname
1 16 260 FishingCompanion
1 18 63 google_analytics.db
Solution
I downloaded the app and play with it while observing the memory via dumpsys.
Things look normal, memory gets reclaimed every time. I can't reproduce the situation, but I do see one spike that is probably related. Whenever you move around in map or zoom (basically refreshing the tiles) in satellite, there will be a brief spike in memory. If you do it fast enough, you don't give a chance for it to get reclaimed and it will go up.
Now, my phone is Android 3.3.4 and have pretty good configuration, so maybe the GC is much more efficient. I wonder though if my older test phones would reclaim the memory slower and thus when I get to the map (say after adding the fishes), I would still have memory from the previous activity that hasn't been reclaimed by GC. Then what I would do, I would go to my location and check things out by zooming in/out. That combined by previous memory from the previous activities might bring the phones to its limit.
This is just a theory though, I am on the road and don't have access to all my test phones. Do you know what version of the phones that are crashing? I'll be back 3-4 days later and I could try the app on my older phones.
UPDATED: I've run more experiments on this app. I'm almost sure that adding the fishes continuously will add more memories. I kept adding and deleting the fishes and checked that the memory keeps going up via dumpsys meminfo. A real users of the Pro Edition or even the lite who keep adding and removing the fishes might eventually hit close to the limit and going to the map afterward will trigger the out memory error since there is a memory jump going into the map. Here is the snapshot after I added and removed the fishes several times
** MEMINFO in pid 11572 [com.hookedroid.fishingcompanion.lite] ** native dalvik other total limit bitmap nativeBmp size: 19728 18251 N/A 37979 32768 N/A N/A allocated: 17174 14674 N/A 31848 N/A 3144 0 free: 405 3577 N/A 3982 N/A N/A N/A (Pss): 12750 1771 25944 40465 N/A N/A N/A (shared dirty): 908 1544 5800 8252 N/A N/A N/A (priv dirty): 12732 1008 24208 37948 N/A N/A N/A
Your private memory jump to total of 37,948 which I am sure if I continue adding and removing fishes, it will throw the OutOfMemoryException eventually
MORE UPDATE (few minutes later): I manage to crash the app using the theory above. I must have added and removed fishes several times before it occurs. It could be more than 50 fishes before the app crashed.
My guess is the SQL somehow didn't get cleaned properly. Looking at the dumpsys after each set of adding and removing 10 fishes (which is the limit of the lite version), I see that
SQL heap: 6581 MEMORY_USED: 6581 PAGECACHE_OVERFLOW: 173 MALLOC_SIZE: 50 DATABASES pgsz dbsz Lookaside(b) Dbname 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 33 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion 1 16 62 FishingCompanion
The SQL memory keeps going up even though I deleted the fishes already. If I keep doing this for some time, eventually it will hit the upper limit of the phone and going to the map (which cause the jump in the memory) will trigger the out of memory exception seemingly indicating that the map page is the cause whereas I think that the add/remove fish page is part of the real cause (I say "part of the real cause" as I don't know if similar effect would occur say if I add new location).
I got the OutMemoryException right about when the total memory is about 58MB (this is probably different from phone to phone). For a reference, here is a similar OutOfMemoryException that I got:
D/dalvikvm(11572): GC_FOR_MALLOC freed 125K, 11% free 25734K/28743K, external 4047K/4695K, paused 188ms D/AndroidRuntime(11572): Shutting down VM W/dalvikvm(11572): threadid=1: thread exiting with uncaught exception (group=0x4001d648) E/AndroidRuntime(11572): FATAL EXCEPTION: main E/AndroidRuntime(11572): java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=28743KB, Allocated=25734KB, Bitmap Size=4047KB) E/AndroidRuntime(11572): at android.graphics.Bitmap.nativeCreate(Native Method) E/AndroidRuntime(11572): at android.graphics.Bitmap.createBitmap(Bitmap.java:695) E/AndroidRuntime(11572): at com.google.android.maps.ZoomHelper.createSnapshot(ZoomHelper.java:444) E/AndroidRuntime(11572): at com.google.android.maps.ZoomHelper.beginZoom(ZoomHelper.java:194) E/AndroidRuntime(11572): at com.google.android.maps.MapView$2.onScaleBegin(MapView.java:380) E/AndroidRuntime(11572): at android.view.ScaleGestureDetector.onTouchEvent(ScaleGestureDetector.java:216) E/AndroidRuntime(11572): at com.google.android.maps.MapView.onTouchEvent(MapView.java:682) E/AndroidRuntime(11572): at android.view.View.dispatchTouchEvent(View.java:3932) E/AndroidRuntime(11572): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:955) E/AndroidRuntime(11572): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015) E/AndroidRuntime(11572): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015) E/AndroidRuntime(11572): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1015)
Hope it helps
Answered By - momo
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.