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开发人员不用担心的内部实现细节.
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).
Scanning the following LogCat however seems to show Dalvik GC running at least in part quite often
正确.特别是在较新的Android版本上,GC以自己的线程同时运行,而不是先前采取的停止世界的方法.
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内存.
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操作系统版本和屏幕分辨率.
Could it be that the Bitmap loading process has its own memory allocation cap?
不,它可以从相同的堆大小预算中起作用.从Android 3.0开始,它真的将与其他Dalvik对象使用的内存一样加载 – 以前,它使用堆外部的系统RAM块,但是这个空间是根据堆的大小预算计算的.
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
生活就像他们说的那样继续下去.
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.
没有.
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内存管理演示文稿,您可能希望观看.
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.这种情况符合我以前的副标题不是“正常情况”.