Android GC注意事项——GC何时运行,它的运行状态是否可以从代码中跟踪



我一直在寻找描述Dalvik虚拟机垃圾收集器架构的详细设计文档,但是没有找到很多。考虑到GC运行对性能的影响,我真的很想更好地理解5个具体问题: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位图加载期间特别遇到内存不足的错误…这对我来说没有意义,因为GC最近运行并且没有分配任何东西,因为应该使VM的容量在3MB (256 MB)以内。在虚拟机崩溃之前,是否只有一小部分256 MB的系统内存被分配给虚拟机?可能是位图加载过程有自己的内存分配上限吗?我知道对象池是在游戏循环中避免GC的好方法,但在不知道Dalvik GC触发的确切原因的情况下,我们仍然对操作系统和Google关于性能最佳实践的模糊讨论充满信心。

  • GC状态(例如:"即将运行","运行中","完成运行")从代码跟踪,以便大的资源分配可能围绕可用内存进行战略规划?我读过一篇关于这个问题的文章:确定Android GC何时运行,这提供了一个有趣的潜在解决方案,但仍然依赖于一个"技巧"。我想知道是否有一个受支持的API调用,可以在生产代码中依赖(不只是调试)来跟踪垃圾收集器的精确状态。如果可以检查GC状态,System.gc()在某些情况下可能有用;否则,因为它不能保证立即运行GC,所以它的有用性会下降很多。

  • 是GC总是系统范围的,还是可以单独的线程(如一个游戏的专用渲染线程)逃避由GC引起的潜在性能滞后问题?

  • 假设如下:"我有一个对象,它的成本(VM RAM预算)/2字节实例化,我立即实例化它与一个单一的引用。然后,我将该引用清空,使对象符合GC的条件,但当然还没有真正释放其内存。然后我立即再次实例化该对象。"这会导致虚拟机崩溃吗?或者操作系统会自动处理这种极端情况以避免虚拟机崩溃?如果操作系统不能处理它,我会引用这个例子来说明为什么我上面的问题#2是有效的;如果可以跟踪GC状态,则可以在源代码中包含逻辑来处理大型对象分配问题(实际上更可能是大型资源,而不是设计糟糕的类),方法是在加载新的大型对象实例之前检查符合GC条件的对象的内存是否已被释放,并在后台轮询GC时显示一个小的加载动画。这应该避免应用程序不响应错误以及合法的内存不足错误…某种类型的onGC()侦听器将是理想的;GC侦听器可以在不重新构建操作系统内核的情况下在本机代码中实现吗?

  • 5。最后,一些源代码…我是否对性能高效的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, new 
    LayoutParams(LayoutParams.MATCH_PARENT,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, holderRL, 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, holderRL, 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>
    

    谢谢,CCJ

      在Android中究竟是什么触发GC ?

    这是SDK开发人员不应该担心的内部实现细节。

    我所见过的其他VM实现通常允许在GC接收到运行信号之前将一定百分比的系统内存分配给应用程序。

    我相信你的话。Java不这样做。JVM并不关心存在多少系统内存——它只关心它自己的VM的潜在堆大小(例如,-Xmx),最多。

    然而,扫描下面的LogCat似乎显示Dalvik GC至少经常部分运行

    正确的。特别是在新版本的Android上,GC在自己的线程中并发运行,而不是之前采用的停止世界的方法。

    这对我来说没有意义,因为GC最近运行并且没有分配任何东西,因为应该使VM的容量在3MB (256 MB)以内。

    不太可能为VM提供256MB的堆空间。根据您的设备,它可能低至16MB。

    此外,Android没有压缩GC算法,所以即使你可能有超过3MB的可用块,你也可能没有连续的3MB块。

    这就是为什么recycle()您的Bitmap对象或尝试重用它们(例如,BitmapOptionsinBitmap,在API Level 11中添加)很重要的原因。

    此外,您可以使用DDMS创建堆转储并使用MAT检查它,以更精确地确定内存的去向以及谁持有哪些内容。这在Android 3.0+上工作得更好,因为MAT将能够在这些版本中更准确地报告Bitmap内存。

    在虚拟机崩溃之前,是否只有一小部分256 MB的系统内存被实际分配给虚拟机?

    是的。它被称为堆。Android设备有堆大小限制。通常,它在16-48MB范围内,具体取决于Android操作系统版本和屏幕分辨率。

    可能是位图加载过程有自己的内存分配上限吗?

    不,它在相同的堆大小预算下工作。从Android 3.0开始,它确实从与其他Dalvik对象使用的相同堆中加载内存——以前,它使用堆外的系统RAM块,但空间是根据堆的大小预算计算的。

    但是在不知道Dalvik GC的确切触发因素的情况下,我们仍然对操作系统和Google关于性能最佳实践的模糊讨论抱有很大的信心

    俗话说,生活还要继续。

    GC状态(例如:"即将运行","运行中","完成运行")从代码跟踪,以便大的资源分配可能围绕可用内存进行战略规划?…我想知道是否有一个支持的API调用,可以在生产代码中依赖(不只是调试)来跟踪垃圾收集器的精确状态。

    GC是否总是系统范围的,或者单独的线程(如游戏的专用渲染线程)是否可以避免由GC引起的潜在性能滞后问题?

    对于任何VM,

    GC从来不是"系统范围的"。GC总是在VM内。

    在新版本的Android上,GC是并发的,因此在正常情况下不会阻塞任何线程。在旧版本的Android上,GC是停止世界并且会影响所有线程。这种变化肯定是在Android 3.0中出现的——我记不清并发GC是否已经在Android 2.3中出现了。有一个关于Android内存管理的2011年Google I|O演示,你可能希望观看。

    这会导致虚拟机崩溃吗?或者操作系统会自动处理这种极端情况以避免虚拟机崩溃吗?

    Android应该在触发OutOfMemoryException之前强制立即GC。根据我前面的段落,这种情况不属于"正常情况"。

    最新更新