1.什么在 Android中触发GC?我看到的其他VM实现通常允许在GC接收到运行信号之前将一定百分比的系统内存分配给应用程序.扫描以下LogCat似乎显示Dalvik GC至少部分经常运行 –
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
正如你所看到的,在〜3 MB的位图加载中我特别遇到了一个outofmemory错误…这是没有意义的,因为GC最近运行,没有分配,因为应该使虚拟机在3MB的容量(256 MB).这个256 MB的系统RAM是否只有一小部分实际上是在VM崩溃之前给予的?可能是Bitmap加载过程有自己的内存分配上限吗?
我知道对象池是一个很好的方法来避免游戏循环中的GC,但是不知道什么触发了Dalvik GC,我们仍然对操作系统和Google对性能最佳实践的模糊讨论感到非常信赖.
>可以从代码跟踪GC状态(例如“运行”,“运行”,“完成运行”),以便可以围绕可用内存策略性地计划大量资源分配?我已经看过这个帖子:Determine when the Android GC runs提供了一个有趣的潜在解决方案,但仍然依赖于“伎俩”.我想知道是否有一个支持的API调用,可以在生产代码(而不仅仅是调试)中依赖于跟踪精确状态的垃圾收集器.在某些情况下,System.gc()可能有用,可以检查IF状态;否则,因为它不能承诺立即GC运行,其有用性下降相当多.
> GC总是系统范围内的,还是可以分离线程(如游戏的专用渲染线程),避免GC引起的潜在性能滞后问题?
鉴于以下假设情景:
“我有一个要花费(VM RAM预算)/ 2个字节来实例化的对象,并且我用一个引用立即实例化它.然后,我将该引用清空,使该对象符合GC标准,但当然也不会释放其内存.然后我立即再次实例化对象.
这会使VM崩溃,还是在某种程度上操作系统自动处理这种极端情况,以避免崩溃VM?如果操作系统没有处理它,我会引用它作为一个很好的例子,为什么我上面的问题#2是有效的;如果可以跟踪GC状态,则可以在源中包含逻辑来处理巨大的对象分配问题(实际上比设计糟糕的类更可能是大的资源),通过查看GC加载对象的内存是否在加载之前被释放新的巨大对象实例,并在后台轮询GC时显示小的加载动画.这应该避免应用程序没有响应错误以及合法的内存错误…某种类型的onGC()侦听器将是理想的;可以在本地代码中实现GC监听器,而无需重新构建操作系统内核?
最后,一些源代码…我有正确的想法,以提高性能的Android编程?
活动类别:
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(); } } }
教程视图管理器类:
/* * 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.cBox); } return description; } public TextView getItem() { if(null == item){ item = (TextView) mRow.findViewById(R.id.cBox2); } 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; } }
教程视图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>
谢谢,
CCJ
解决方法
- What exactly triggers GC in Android?
这是SDK开发人员不用担心的内部实现细节.
@H_403_35@Other VM implementations I’ve seen usually allow for a certain percentage of system memory to be allocated to an application before their GC receives a signal to run.
我会接受你的话. Java不这样做. JVM不关心存在多少系统内存 – 它最多仅关心其自己的VM的潜在堆大小(例如,-Xmx).
@H_403_35@Scanning the following LogCat however seems to show Dalvik GC running at least in part quite often
正确.特别是在较新的Android版本上,GC以自己的线程同时运行,而不是先前采取的停止世界的方法.
@H_403_35@This doesn’t make sense to me since GC recently ran and nothing allocated since should have brought the VM within 3MB of capacity (256 MB).
您的VM极不可能拥有256MB的堆空间.根据您的设备,它可能低至16MB.
此外,Android没有压缩GC算法,因此即使可能有3MB以上的可用性,您可能没有连续的3MB块.
这就是为什么要重新利用()您的Bitmap对象或尝试重用它们(例如,BitmapOptions的映射,添加在API级别11中).
此外,您可以使用DDMS创建堆转储和MAT检查它,更准确地确定您的内存在哪里,以及谁持有什么.这在Android 3.0上更好,因为MAT将能够更准确地在这些版本中报告Bitmap内存.
@H_403_35@Is there only a small percentage of that 256 MB system RAM which is actually given to the VM before it crashes?
是.它被称为堆. Android设备的堆大小限制.通常情况下,它的范围是16-48MB,具体取决于Android操作系统版本和屏幕分辨率.
@H_403_35@Could it be that the Bitmap loading process has its own memory allocation cap?
不,它可以从相同的堆大小预算中起作用.从Android 3.0开始,它真的将与其他Dalvik对象使用的内存一样加载 – 以前,它使用堆外部的系统RAM块,但是这个空间是根据堆的大小预算计算的.
@H_403_35@but without knowing EXACTLY what triggers Dalvik GC we’re still placing an awful lot of faith in the OS and Google’s vague discussions of performance best-practices
生活就像他们说的那样继续下去.
@H_403_35@Can the GC state (e.g. ‘about to run’,‘running’,‘finished running’) be tracked from code so that large resource allocations might be planned strategically around available memory? … I’d like to know if there is a supported API call somewhere which can be relied upon in production code (not just debug) to track the precise state of the garbage collector.
没有.
@H_403_35@Is GC always system-wide,or can separate threads (such as a dedicated rendering thread for a game) escape the potential performance lag issues caused by GC?
GC对于任何虚拟机来说都不是“系统范围”. GC始终位于VM内.
在较新的Android版本中,GC是并发的,因此在正常情况下不会实质阻止任何线程.在旧版本的Android上,GC是世界上的一切,将影响所有的线程. Android 3.0的变化肯定是适用的 – 对于并发GC是否已经适用于Android 2.3,我的记忆是模糊的.有一个2011年Google I | O的Android内存管理演示文稿,您可能希望观看.
@H_403_35@Would this crash the VM or is there some way the OS handles such extreme situations automatically to avoid crashing the VM?
在升级OutOfMemoryException之前,Android应强制立即使用GC.这种情况符合我以前的副标题不是“正常情况”.