Android GC注意事项 - GC何时运行,是否可以从代码中跟踪其运行状态?

时间:2011-12-14 18:39:31

标签: java android garbage-collection

我一直在寻找一些时间来描述Dalvik VM垃圾收集器架构的详细设计文档,但还没有发现太多。考虑到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的系统RAM在VM崩溃之前实际给予它?可能是Bitmap加载过程有自己的内存分配上限吗? 我知道对象池是在游戏循环期间尝试避免GC的一种好方法,但是如果不知道是什么触发了Dalvik GC,我们仍然对操作系统和Google对性能最佳实践的模糊讨论抱有极大的信心。

  1. 是否可以从代码中跟踪GC状态(例如“即将运行”,“正在运行”,“已完成运行”),以便可以围绕可用内存战略性地规划大量资源分配?我已经阅读过关于此事的帖子:Determine when the Android GC runs提供了一个有趣的潜在解决方案,但仍然依赖于“技巧”。我想知道在某个地方是否存在支持的API调用,可以依赖于生产代码(而不仅仅是调试)来跟踪垃圾收集器的精确状态。 System.gc()在某些情况下可能有用,如果可以检查GC状态;否则,因为它不能保证立即运行GC,其实用性会下降很多。

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

  3. 鉴于以下假设情景: '我有一个成本(VM RAM预算)/ 2个字节来实例化的对象,我用一个引用立即实例化它。然后我将该引用置空,使该对象符合GC的条件,但当然还没有实际释放它的内存。然后我再次立即实例化该对象。 这会使VM崩溃还是某种方式操作系统会自动处理这种极端情况以避免崩溃VM?如果操作系统没有处理它,我会举几个例子来解释为什么我的问题#2有效;如果可以跟踪GC状态,则可以通过检查在加载之前是否已释放来自GC符合条件的对象的内存来处理源中的逻辑以处理大量对象分配问题(实际上更可能是比设计错误的类更大的资源)新的巨大对象实例,并在后台轮询GC时显示一个小的加载动画。这应该避免应用程序没有响应错误以及合法的内存不足错误...某种onGC()监听器将是理想的;可以在本机代码中实现GC侦听器而无需重新构建操作系统内核吗?

  4. 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;
    }
    }
    

    教程查看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

1 个答案:

答案 0 :(得分:23)

  
      
  1. 究竟是什么触发了Android中的GC?
  2.   

这是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() BitmapinBitmap个对象或尝试重用它们的重要原因(例如,BitmapOptions的{​​{1}},在API级别11中添加。

此外,您可以使用DDMS创建堆转储和MAT来检查它,以更准确地确定您的内存在哪里以及谁在抓住什么。这在Android 3.0+上效果更好,因为MAT可以在这些版本中更准确地报告Bitmap内存。

  

只有一小部分256 MB的系统RAM在VM崩溃之前实际上给了它吗?

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

  

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

不,它使用相同的堆大小预算。从Android 3.0开始,它确实将内存从与Dalvik对象的其余部分相同的堆中加载 - 以前,它使用堆外部的系统RAM块,但是空间是根据堆的大小预算计算的。 / p>

  

但我们不知道是什么触发了Dalvik GC,我们仍然对操作系统和Google对性能最佳实践的模糊讨论抱有极大的信心

正如他们所说,生活还在继续。

  

是否可以从代码中跟踪GC状态(例如“即将运行”,“正在运行”,“已完成运行”),以便可以围绕可用内存战略性地规划大量资源分配? ...我想知道某个地方是否存在支持的API调用,可以依赖于生产代码(而不仅仅是调试)来跟踪垃圾收集器的精确状态。

没有

  

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

对于任何VM,GC都不是“系统范围的”。 GC始终位于VM中。

在较新版本的Android上,GC是并发的,因此在正常情况下不会严重阻止任何线程。在旧版Android上,GC是世界各地的,会影​​响所有线程。对于Android 3.0而言,这种变化肯定已经到位 - 我的内存对于并发GC是否已经适用于Android 2.3而言仍然很模糊。您可能希望观看2011年关于Android内存管理的Google I | O演示文稿。

  

这会导致虚拟机崩溃,还是某种方式操作系统会自动处理这种极端情况以避免崩溃虚拟机?

Android应该在提升OutOfMemoryException之前立即强制执行GC。根据我之前的paragaraph,这种情况不符合“正常情况”。