Java – Android GC considerations – when does the GC run and can track its running status from the code?
I've been looking for some detailed design documents describing the architecture of Dalvik VM garbage collector, but I haven't become much Considering the performance impact of GC operation, I really want to better understand five specific problems:
12-14 11:34:57.753: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 735 objects / 54272 bytes in 90ms 12-14 11:34:57.893: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 256 objects / 12240 bytes in 61ms 12-14 11:34:57.943: I/jPCT-AE(279): Loading Texture... 12-14 11:34:57.993: D/dalvikvm(279): GC_FOR_MALLOC freed 65 objects / 2840 bytes in 52ms 12-14 11:34:58.013: I/dalvikvm-heap(279): Grow heap (frag case) to 5.039MB for 1048592-byte allocation 12-14 11:34:58.073: D/dalvikvm(279): GC_FOR_MALLOC freed 1 objects / 40 bytes in 59ms 12-14 11:34:58.243: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 11 objects / 432 bytes in 55ms 12-14 11:34:58.283: I/jPCT-AE(279): Loading Texture... 12-14 11:34:58.333: D/dalvikvm(279): GC_FOR_MALLOC freed 10 objects / 416 bytes in 46ms 12-14 11:34:58.344: I/dalvikvm-heap(279): Grow heap (frag case) to 6.040MB for 1048592-byte allocation 12-14 11:34:58.423: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms 12-14 11:34:58.563: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 10 objects / 384 bytes in 47ms 12-14 11:34:58.603: I/jPCT-AE(279): Loading Texture... 12-14 11:34:58.653: D/dalvikvm(279): GC_FOR_MALLOC freed 11 objects / 464 bytes in 44ms 12-14 11:34:58.663: I/dalvikvm-heap(279): Grow heap (frag case) to 7.040MB for 1048592-byte allocation 12-14 11:34:58.743: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms 12-14 11:34:58.973: I/System.out(279): started document! ... 12-14 11:43:05.393: I/jPCT-AE(279): Memory usage before compacting: 5867 KB used out of 6215 KB 12-14 11:43:05.453: D/dalvikvm(279): GC_EXPLICIT freed 2560 objects / 145712 bytes in 61ms 12-14 11:43:05.503: D/dalvikvm(279): GC_EXPLICIT freed 295 objects / 21448 bytes in 51ms 12-14 11:43:05.717: I/jPCT-AE(279): Memory usage after compacting: 5705 KB used out of 6215 KB ... 12-14 11:43:05.792: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 105 objects / 6152 bytes in 56ms 12-14 11:43:05.855: D/dalvikvm(279): GC_FOR_MALLOC freed 3 objects / 80 bytes in 51ms ... 12-14 11:43:12.863: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 864 objects / 1099072 bytes in 70ms 12-14 11:43:13.053: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 45 objects / 1760 bytes in 55ms 12-14 11:43:14.533: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 49 objects / 2376 bytes in 58ms 12-14 11:43:14.933: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 34 objects / 1408 bytes in 55ms 12-14 11:43:15.423: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 504 bytes in 58ms 12-14 11:43:15.953: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 520 bytes in 56ms ... 12-14 11:43:31.203: I/jPCT-AE(279): Visibility lists disposed! 12-14 11:43:31.203: I/jPCT-AE(279): All texture data unloaded from gpu! 12-14 11:43:31.203: I/jPCT-AE(279): Renderer disposed! 12-14 11:43:31.203: I/jPCT-AE(279): Static references cleared... ... 12-14 11:43:36.943: E/dalvikvm-heap(279): 2964320-byte external allocation too large for this process. 12-14 11:43:36.953: E/GraphicsJNI(279): VM won't let us allocate 2964320 bytes 12-14 11:43:36.953: D/AndroidRuntime(279): Shutting down VM 12-14 11:43:36.953: W/dalvikvm(279): threadid=1: thread exiting with uncaught exception (group=0x4001d800) 12-14 11:43:36.973: E/AndroidRuntime(279): FATAL EXCEPTION: main 12-14 11:43:36.973: E/AndroidRuntime(279): android.view.InflateException: Binary XML file line #33: Error inflating class <unkNown> 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.createView(LayoutInflater.java:513) 12-14 11:43:36.973: E/AndroidRuntime(279): at com.android.internal.policy.impl.PhoneLayoutInflater. onCreateView(PhoneLayoutInflater.java:56) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.rInflate(LayoutInflater.java:618) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.rInflate(LayoutInflater.java:621) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.inflate(LayoutInflater.java:407) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.inflate(LayoutInflater.java:320) 12-14 11:43:36.973: E/AndroidRuntime(279): at com.ai.ultimap.views.Manual.onItemClick(Manual.java:467) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.AdapterView.performItemClick(AdapterView.java:284) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.AbsListView$PerformClick.run(AbsListView.java:1696) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.os.Handler.handleCallback(Handler.java:587) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.os.Handler.dispatchMessage(Handler.java:92) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.os.Looper.loop(Looper.java:123) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.app.ActivityThread.main(ActivityThread.java:4627) 12-14 11:43:36.973: E/AndroidRuntime(279): at java.lang.reflect.Method.invokeNative(Native Method) 12-14 11:43:36.973: E/AndroidRuntime(279): at java.lang.reflect.Method.invoke(Method.java:521) 12-14 11:43:36.973: E/AndroidRuntime(279): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 12-14 11:43:36.973: E/AndroidRuntime(279): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 12-14 11:43:36.973: E/AndroidRuntime(279): at dalvik.system.NativeStart.main(Native Method) 12-14 11:43:36.973: E/AndroidRuntime(279): Caused by: java.lang.reflect.InvocationTargetException 12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init> (ImageView.java:108) 12-14 11:43:36.973: E/AndroidRuntime(279): at java.lang.reflect.Constructor.constructNative(Native Method) 12-14 11:43:36.973: E/AndroidRuntime(279): at java.lang.reflect.Constructor.newInstance(Constructor.java:446) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.view.LayoutInflater.createView(LayoutInflater.java:500) 12-14 11:43:36.973: E/AndroidRuntime(279): ... 18 more 12-14 11:43:36.973: E/AndroidRuntime(279): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.Bitmap.nativeCreate(Native Method) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.Bitmap.createBitmap(Bitmap.java:468) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.Bitmap.createBitmap(Bitmap.java:435) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.content.res.Resources.loadDrawable(Resources.java:1709) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init> (ImageView.java:118) 12-14 11:43:36.973: E/AndroidRuntime(279): ... 22 more 12-14 11:43:38.763: I/Process(279): Sending signal. PID: 279 SIG: 9
As you can see, I especially encountered an OUTOFMEMORY error during the bitmap loading of ~ 3MB... This is meaningless because the GC is running recently and has not been allocated, because the virtual machine should have a capacity of 3MB (256 MB) Is only a small portion of this 256 MB system RAM actually given before the VM crashes? Is it possible that the bitmap loading process has its own upper memory allocation limit? I know that object pooling is a good way to avoid GC in game loops, but I don't know what triggers Dalvik GC. We still trust the vague discussion of performance best practices by the operating system and Google
>GC status can be tracked from code (e.g. "run", "run", "finish run") so that large resource allocations can be strategically planned around available memory? I've read this post: determine when the Android GC runs provides an interesting potential solution, but still relies on "tricks" I wonder if there is a supported API call that can rely on a garbage collector that tracks the exact state in production code (not just debugging) In some cases, system GC () may be useful to check if status; Otherwise, because it can't promise to run GC immediately, its usefulness will be greatly reduced. > Is GC always system wide, or can you separate threads (such as special rendering threads for games) to avoid potential performance lag caused by GC? Consider the following scenario: "I have a budget to spend (VM RAM) /2 bytes to instantiate the object, and I instantiate it immediately with a reference Then, I empty the reference to make the object conform to the GC standard, but of course, I won't free its memory Then I instanced the object again immediately Will this cause the VM to crash, or will the operating system automatically handle this extreme situation to some extent to avoid VM crash? If the operating system does not handle it, I will cite it as a good example. Why is my above problem #2 valid; If you can track the GC status, you can include logic in the source to deal with the huge object allocation problem (in fact, it is more likely to be a large resource than a poorly designed class). Check whether the memory of the GC loaded object is released before loading, and display a small loading animation when polling the GC in the background This should avoid application unresponsive errors and legitimate memory errors... Some type of ONGC () listener would be ideal; Can GC listeners be implemented in native code without rebuilding the operating system kernel?
Finally, some source code... Do I have the right idea to improve the performance of Android programming?
Activity category:
package com.ai.ultimap; //imports omitted... public class UltiMapActivity extends Activity { //Housekeeping private String viewDriverID = ""; private static final int TUTORIAL = 7; //visuals private HomeView hv; //home view private ConfigView cv; //config view private MapView mv; //map view private Manual man; //manual view private int manCount = 0; //tracks the number of times the manual has been called //with menu button,ignoring button presses unless value is zero private PathCreator pcv; //path creator view private MasterGL mgl; //the gl center private String pending = "Coming soon..."; private PathCreator draw; private Surfacer morlock; // Used to handle pause and resume... private static UltiMapActivity master; //XML I/O considerations private String fXML = "mypaths.xml"; private String sXML = "data was not saved properly...?"; private FileOutputStream fos; private FileInputStream fis; private FileWriter fw; private FileReader fr; private Date theDate = new Date(); private char[] buf = new char[1]; //Feedback stuffs private FeedbackController Feed; //tracking you... :) private WifiStalk stalk; private long lat; private long longitude; //Testing private DrawView dv; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("me","ultimap created!"); master = null; mgl = new MasterGL(this); //revisit this later for versatility man = new Manual(this); Feed = new FeedbackController(this); stalk = new WifiStalk(this); draw = new PathCreator(this); hv = new HomeView(this,draw); try { BeanCounter bean = new BeanCounter(this); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (XmlPullParserException e) { // TODO Auto-generated catch block e.printStackTrace(); } showDialog(TUTORIAL); } @Override public boolean onKeyDown(int keyCode,KeyEvent e){ if (keyCode == 82){ if (viewDriverID.equals("hv")){ hv.removeHV(); } else if (viewDriverID.equals("cv")){ cv.removeCV(); } else if (viewDriverID.equals("mv")){ return true; } else if (viewDriverID.equals("pcv")){ return true; } if(man.getAddedState() == 0){ //Show the manual code... System.out.println("View we're coming from: " + this.getVDID()); Log.e("me","man.getaddedstate does equal 0,should be about to makeMan"); man.makeMan(); } else if(man.getAddedState() == 2){ man.removeMan(); man.removeMan2(); man.setAddedState(1); } else if(man.getAddedState() == 1){ System.out.println("View we're coming from: " + this.getVDID()); man.addMan(); } } return true; } @Override protected Dialog onCreateDialog(int id) { //alerts ommitted for space } //Used to track the semantic context of what the Activity is displaying //Getters/setters for external access ommitted @Override protected void onStart(){ super.onStart(); Log.d("me","ultimap started!"); } @Override protected void onPause() { super.onPause(); Log.d("me","ultimap paused!"); if (mgl.getGLview() != null){ mgl.getGLview().onPause(); } if (draw.getGLV() != null){ draw.getGLV().onPause(); } } @Override protected void onResume() { super.onResume(); Log.d("me","ultimap resumed!"); stalk.killListener(); if (mgl.getGLview() != null){ mgl.getGLview().onResume(); Log.d("me","mgl.getGLview is NOT null on resume"); } else if (mgl.getGLview() == null){ mgl.initGL(); mgl.getGLview().onResume(); Log.d("me","mgl.getGLview is null on resume"); } if (draw.getGLV() != null){ draw.getGLV().onResume(); Log.d("me","draw.getGLV is NOT null on resume"); } else if (draw.getGLV() == null && draw.getHGL() != null){ draw.pcvInit(); Log.d("me","draw.getGLV is null on resume"); } if (hv.getMV() != null && hv.getMV().getGLV() != null){ hv.getMV().getGLV().onResume(); Log.d("me","map.getGLV is NOT null on resume"); } else if (hv.getMV() != null && hv.getMV().getGLV() == null && hv.getMV().getHGL() != null){ hv.getMV().mvInit(); Log.d("me","map.getGLV is null on resume"); } } @Override protected void onStop() { super.onStop(); //Feed.getSP().release(); Log.d("me","ultimap stopped!"); } @Override protected void onRestart(){ super.onRestart(); Log.d("me","ultimap restarted!"); if (mgl != null){ mgl.initGL(); } } @Override protected void onDestroy(){ super.onDestroy(); Log.d("me","ultimap destroyed!"); mgl.disposeTextures(); if (Feed.getSP() != null && Feed.getSID() != 0 && Feed.getLoaded() == true){ Feed.getSP().unload(Feed.getSID()); Feed.getSP().release(); } } }
Tutorial view manager class:
/* * This class defines an in-app manual which is callable/dismissable * in a non-invasive way... * * http://www.codeproject.com/KB/android/ViewFlipper_Animation.aspx *http://developer.android.com/reference/android/widget/ *ViewFlipper.html#ViewFlipper%28android.content.Context%29 * http://developer.android.com/resources/articles/avoiding-memory-leaks.html */ package com.ai.ultimap.views; //imports ommitted public class Manual extends View implements OnItemClickListener{ private UltiMapActivity hUMA; private ListView lv1; private listadapter la; private LayoutInflater mInflater; private Vector<RowData> data; private TextView tv; private RelativeLayout holderRL; private View v; private View v2; private int addedState = 0; //tracks whether or not a view has been instantiated,//and if so whether or not it is the currently visible view private int addedState2 = 0; //Grid View stuff private GridView helpGrid; //ViewFlipper stuff private ViewFlipper vf; private TextView tutTV; private String mapTutString = "Map View Tutorial Part: "; private String pcTutString = "Path Creator Tutorial Part: "; private String tutType; private TextView counterTV; private int partCounter = 1; private float oldTouchValue = 0.0f; private boolean searchOk = true; private ImageView floatingImage; public Manual(UltiMapActivity hAct){ super(hAct); hUMA = hAct; holderRL = new RelativeLayout(hUMA); v = new View(hUMA); floatingImage = new ImageView(hUMA); } //Here we summon and populate the grid view public void makeMan(){ if (addedState == 0){ Log.e("me","in makeMan"); mInflater = (LayoutInflater) hUMA.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); hUMA.addContentView(holderRL,new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT)); v = mInflater.inflate(R.layout.helpgrid,holderRL,false); helpGrid = (GridView) v.findViewById(R.id.manGV); helpGrid.setAdapter(new ImageAdapter(hUMA)); hUMA.addContentView(v,LayoutParams.MATCH_PARENT)); helpGrid.setOnItemClickListener(this); addedState = 2; } } public void addMan(){ if (v != null && addedState == 1){ v.setVisibility(VISIBLE); v.bringToFront(); addedState = 2; } } public void addMan2(){ if (v2 != null && addedState2 == 1){ v2.setVisibility(VISIBLE); v2.bringToFront(); addedState2 = 2; } } public void removeMan(){ if (v != null && addedState == 2){ v.setVisibility(GONE); addedState = 1; String s = hUMA.getVDID(); if (s.equals("hv")){ hUMA.getHome().addHV(); Log.d("me","add hjomeview called from anual"); Log.d("me","hv addedstate : " + hUMA.getHome().getAddedState()); } else if (s.equals("cv")){ hUMA.getConfig().addCV(); } else if (s.equals("mv")){ hUMA.getHome().getMV().mvInit(); } else if (s.equals("pcv")){ hUMA.getDraw().pcvInit(); } } } public void removeMan2(){ if (v2 != null && addedState2 == 2){ v2.setVisibility(GONE); addedState2 = 1; String s = hUMA.getVDID(); if (s.equals("hv")){ hUMA.getHome().addHV(); Log.d("me","add hjomeview called from manual"); Log.d("me","hv addedstate : " + hUMA.getHome().getAddedState()); } else if (s.equals("cv")){ hUMA.getConfig().addCV(); } else if (s.equals("mv")){ hUMA.getHome().getMV().mvInit(); } else if (s.equals("pcv")){ hUMA.getDraw().pcvInit(); } } } //addedstate getters and setters ommitted for space @Override public boolean onTouchEvent(MotionEvent touchevent) { switch (touchevent.getAction()) { case MotionEvent.ACTION_DOWN: { System.out.println("received a touch down at " + touchevent.getX() + "," + touchevent.getY()); oldTouchValue = touchevent.getX(); if(this.searchOk==false) return false; float currentX = touchevent.getX(); if (currentX > (vf.getWidth()/2)) { vf.setInAnimation(AnimationHelper.inFromRightAnimation()); vf.setOutAnimation(AnimationHelper.outToLeftAnimation()); vf.showNext(); if (partCounter <= 3 && partCounter >= 1){ partCounter++; } else if (partCounter == 4){ partCounter = 1; } else{ Log.e("me","partCounter got past 4..."); } if(tutType.equals("map")){ counterTV.setText(mapTutString + partCounter); } else if(tutType.equals("pc")){ counterTV.setText(pcTutString + partCounter); } else{ Log.e("me","not getting valid tutType string"); } } if (currentX <= (vf.getWidth()/2)) { vf.setInAnimation(AnimationHelper.inFromLeftAnimation()); vf.setOutAnimation(AnimationHelper.outToRightAnimation()); vf.showPrevIoUs(); if (partCounter >= 2 && partCounter <= 4){ partCounter--; } else if (partCounter == 1){ partCounter = 4; } else{ Log.e("me","partCounter got below 1..."); } if(tutType.equals("map")){ counterTV.setText(mapTutString + partCounter); } else if(tutType.equals("pc")){ counterTV.setText(pcTutString + partCounter); } else{ Log.e("me","not getting valid tutType string"); } } break; } case MotionEvent.ACTION_UP: { //nothing to do here } } return false; } public void setUserText(String str){ tv.setText(str); } private class CustomTV extends TextView{ private String content = ""; public CustomTV(Context c,String str){ super(c); content = str; this.setText(content); } } /** * Data type used for custom adapter. Single item of the adapter. */ private class RowData { protected String mItem; protected String mDescription; RowData(String item,String description){ mItem = item; mDescription = description; } @Override public String toString() { return mItem + " " + mDescription; } } private class CustomAdapter extends ArrayAdapter<RowData> { public CustomAdapter(Context context,int resource,int textViewResourceId,List<RowData> objects) { super(context,resource,textViewResourceId,objects); } @Override public View getView(int position,View convertView,ViewGroup parent) { ViewHolder holder = null; //widgets displayed by each item in your list TextView item = null; TextView description = null; //data from your adapter RowData rowData= getItem(position); //we want to reuse already constructed row views... if(null == convertView){ convertView = mInflater.inflate(R.layout.custom_row,null); holder = new ViewHolder(convertView); convertView.setTag(holder); } holder = (ViewHolder) convertView.getTag(); item = holder.getItem(); item.setText(rowData.mItem); description = holder.getDescription(); description.setText(rowData.mDescription); return convertView; } } /** * Wrapper for row data. * */ private class ViewHolder { private View mRow; private TextView description = null; private TextView item = null; public ViewHolder(View row) { mRow = row; } public TextView getDescription() { if(null == description){ description = (TextView) mRow.findViewById(R.id.c@R_654_2419@); } return description; } public TextView getItem() { if(null == item){ item = (TextView) mRow.findViewById(R.id.c@R_654_2419@2); } return item; } } @Override public void onItemClick(AdapterView<?> arg0,View arg1,int position,long id) { v.setVisibility(GONE); if (addedState2 == 0){ hUMA.addContentView(this,DefineLayoutParams.getParams(DefineLayoutParams.getMM())); //this is why the onTouch only starts lsitening at this point if (position == 0){ v2 = mInflater.inflate(R.layout.flipper,false); vf = (ViewFlipper) v2.findViewById(R.id.manFlipperVF); tutTV = (TextView) v2.findViewById(R.id.manDescriptionTV); counterTV = (TextView) v2.findViewById(R.id.mapviewtutCounterTV); tutTV.setText("Map View Instructions: ..."); counterTV.setText(mapTutString + partCounter); tutType = "map"; } else if (position == 1){ v2 = mInflater.inflate(R.layout.flipperpc,false); vf = (ViewFlipper) v2.findViewById(R.id.manFlipperpcVF); tutTV = (TextView) v2.findViewById(R.id.manDescriptionpcTV); counterTV = (TextView) v2.findViewById(R.id.manFlipperCounterpcTV); tutTV.setText("Path Creator Tutorial:..."); counterTV.setText(pcTutString + partCounter); tutType = "pc"; } addedState2 = 2; hUMA.addContentView(v2,DefineLayoutParams.getParams(DefineLayoutParams.getWW())); } else if(addedState2 == 1){ v2.setVisibility(VISIBLE); addedState2 = 2; } } public String getTutType(){ return tutType; } }
Tutorial view flipper XML:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ScrollView android:id="@+id/manDerscriptionSV" android:layout_width="match_parent" android:layout_height="200px" > <TextView android:id="@+id/manDescriptionTV" android:text="Coming Soon..." android:layout_width="match_parent" android:layout_height="wrap_content" /> </ScrollView> <TextView android:id="@+id/mapviewtutCounterTV" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Map View Tutorial Part: " android:gravity="center" android:layout_below="@id/manDerscriptionSV" /> <ViewFlipper android:id="@+id/manFlipperVF" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/mapviewtutCounterTV" > <ImageView android:id="@+id/mapviewtut1" android:src="@drawable/mapviewtutflipper1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/mapviewtut2" android:src="@drawable/mapviewtutflipper2" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/mapviewtut3" android:src="@drawable/mapviewtutflipper3" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:id="@+id/mapviewtut4" android:src="@drawable/mapviewtutflipper4" android:layout_width="match_parent" android:layout_height="wrap_content" /> </ViewFlipper> </RelativeLayout>
Thank you, CCJ
Solution
This is the internal implementation details that SDK developers don't have to worry about
I will accept your words Java does not The JVM doesn't care how much system memory exists - it only cares about the potential heap size of its own VM (for example, - Xmx)
correct. Especially on the newer Android version, GC runs with its own thread at the same time, rather than the previous method of stopping the world
Your VM is unlikely to have 256 MB of heap space Depending on your device, it may be as low as 16MB
In addition, Android does not have a compressed GC algorithm, so even if you may have more than 3MB availability, you may not have consecutive 3MB blocks
That's why you reuse () your bitmap objects or try to reuse them (for example, the mapping of bitmapoptions, added at API level 11)
In addition, you can use DDMS to create a heap dump and mat to check it to more accurately determine where your memory is and who holds what This is better on Android 3.0 because mat will be able to report bitmap memory more accurately in these versions
Yes It is called a heap Heap size limit for Android devices Typically, it ranges from 16 to 48 MB, depending on the Android operating system version and screen resolution
No, it works from the same heap size budget Starting with Android 3.0, it will really load like the memory used by other Dalvik objects - previously, it used system RAM blocks outside the heap, but this space is calculated according to the heap size budget
Life goes on as they say
No,
GC is not "system wide" for any virtual machine GC is always in VM
In the newer Android version, GC is concurrent, so it will not substantially block any threads under normal circumstances In the old version of Android, GC is everything in the world and will affect all threads The Android 3.0 changes are certainly applicable - my memory is blurred as to whether concurrent GC has been applied to Android 2.3 There is a 2011 Google I | o Android memory management presentation that you may want to watch
Android should force GC to be used immediately before upgrading OutOfMemoryException This situation is consistent with my previous subtitle, not "normal"