static class VMRuntimeHack { private Object runtime = null; private Method trackAllocation = null; private Method trackFree = null; public boolean trackAlloc(long size) { if (runtime == null) return false; try { Object res = trackAllocation.invoke(runtime, Long.valueOf(size)); return (res instanceof Boolean) ? (Boolean)res : true; } catch (IllegalArgumentException e) { return false; } catch (IllegalAccessException e) { return false; } catch (InvocationTargetException e) { return false; } } public boolean trackFree(long size) { if (runtime == null) return false; try { Object res = trackFree.invoke(runtime, Long.valueOf(size)); return (res instanceof Boolean) ? (Boolean)res : true; } catch (IllegalArgumentException e) { return false; } catch (IllegalAccessException e) { return false; } catch (InvocationTargetException e) { return false; } } public VMRuntimeHack() { boolean success = false; try { Class cl = Class.forName("dalvik.system.VMRuntime"); Method getRt = cl.getMethod("getRuntime", new Class[0]); runtime = getRt.invoke(null, new Object[0]); trackAllocation = cl.getMethod("trackExternalAllocation", new Class[] {long.class}); trackFree = cl.getMethod("trackExternalFree", new Class[] {long.class}); success = true; } catch (ClassNotFoundException e) { } catch (SecurityException e) { } catch (NoSuchMethodException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } if (!success) { Log.i(TAG, "VMRuntime hack does not work!"); runtime = null; trackAllocation = null; trackFree = null; } } } private static final VMRuntimeHack runtime = new VMRuntimeHack();
static class BitmapFactory { public BitmapFactory(boolean useHack) { this.useHack = useHack; } // public Bitmap alloc(int dx, int dy) { Bitmap bmp = Bitmap.createBitmap(dx, dy, Bitmap.Config.RGB_565); if (useHack) { runtime.trackFree(bmp.getRowBytes() * bmp.getHeight()); hackedBitmaps.add(bmp); } allocatedBitmaps.add(bmp); return bmp; } // public void free(Bitmap bmp) { bmp.recycle(); if (hackedBitmaps.contains(bmp)) { runtime.trackAlloc(bmp.getRowBytes() * bmp.getHeight()); hackedBitmaps.remove(bmp); } allocatedBitmaps.remove(bmp); } // ( ) public void freeAll() { for (Bitmap bmp : new LinkedList<Bitmap>(allocatedBitmaps)) free(bmp); } private final boolean useHack; private Set<Bitmap> allocatedBitmaps = new HashSet<Bitmap>(); private Set<Bitmap> hackedBitmaps = new HashSet<Bitmap>(); }
public int testAllocation(boolean useHack, int maxAlloc) { System.gc(); BitmapFactory factory = new BitmapFactory(useHack); int allocated = 0; // AndroidRuntime: java.lang.OutOfMemoryError: bitmap size exceeds VM budget while (allocated < maxAlloc) { try { Bitmap bmp = factory.alloc(1024, 512); allocated += bmp.getRowBytes() * bmp.getHeight(); Log.i(TAG, "Bitmap bytes allocated " + allocated); } catch (OutOfMemoryError e) { Log.e(TAG, "Exception while allocation of bitmap, total size = " + allocated, e); break; } } factory.freeAll(); return allocated; }
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // perform test int allocatedNormally = testAllocation(false, 48 * MB); int allocatedWithHack = testAllocation(true, 48 * MB); String msg = "normally: " + (allocatedNormally / MB) + " MB allocated " + "\nwith hack: " + (allocatedWithHack / MB) + " MB allocated"; Log.i(TAG, msg); // display results LayoutInflater inflater = LayoutInflater.from(this); View main = (View)inflater.inflate(R.layout.main, null); TextView text = (TextView)main.findViewById(R.id.text); text.setText(msg); setContentView(main); }
03-07 09:43:37.233: I/bmphack(17873): normally: 10 MB allocated
03-07 09:43:37.233: I/bmphack(17873): with hack: 48 MB allocated
Source: https://habr.com/ru/post/139717/
All Articles