2011-12-14 80 views
9

我一直在尋找一些時間來描述Dalvik虛擬機的垃圾收集器的體系結構的詳細設計文檔,但沒有太多發現。鑑於GC運行的性能影響,我真的想更好地理解5個具體問題: 1.究竟是什麼觸發了Android中的GC?我見過的其他VM實現通常允許在GC接收到運行信號之前將一定百分比的系統內存分配給應用程序。掃描以下logcat的但是似乎表明Dalvik的GC運行至少部分相當下意識的Android GC注意事項 - 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最近運行並沒有分配任何內容,因爲它應該將虛擬機的容量限制在3MB以內(256 MB)。是否只有一小部分的256 MB系統RAM在崩潰之前實際提供給VM?難道是位圖加載過程有自己的內存分配上限? 我知道對象池是在遊戲循環中嘗試避免GC的一種好方法,但不知道是什麼觸發了Dalvik GC,我們仍然對操作系統和Google對性能最佳實踐的模糊討論抱有極大的信心。

  • 能否GC狀態(例如「即將運行」,「運行」,「運行完畢」)從代碼追蹤,以便大資源分配可能會策略性地周圍可用的計劃記憶?我已閱讀這篇文章:Determine when the Android GC runs它提供了一個有趣的潛在解決方案,但仍然依賴於一個'技巧'。我想知道是否有支持的API調用可以依賴生產代碼(不僅僅是調試)來跟蹤垃圾收集器的準確狀態。 System.gc()在某些情況下可能會有用如果可以檢查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; 
    } 
    } 
    

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

    +0

    非常好的問題。 – Mob

    +0

    作爲這個方向的部分領導者:Android上的每個進程都有自己的虛擬機實例,即自己的GC。 – BRFennPocock

    +0

    保持對象引用比您需要的時間更長通常不是一個好主意。諸如'ViewInflator'之類的東西以及「xml I \ O」註釋下的變量在我看來就像是臨時使用的東西,然後可以被丟棄。如果可能,請刪除這些字段並將它們設置爲局部變量,以便儘快進行垃圾回收。 –

    回答

    23
    1. 究竟是什麼觸發了Android中的GC?

    這是SDK開發人員不應該擔心的內部實現細節。

    我見過的其他虛擬機實現通常允許一定比例的系統內存在GC接收到運行信號之前分配給應用程序。

    我會對此表示贊同。 Java不像這樣。 JVM並不關心繫統內存的多少 - 它只關心其自身虛擬機的潛在堆大小(例如,-Xmx)。

    然而掃描下面的logcat似乎顯示的Dalvik GC運行至少部分往往

    正確的。特別是在較新版本的Android中,GC可以在自己的線程中同時運行,而不是早先採用的「停止世界」方法。

    這對我來說沒有任何意義,因爲GC最近運行並沒有進行任何分配,因爲它應該在3MB容量(256 MB)之內引入VM。

    您的虛擬機擁有256MB的堆空間是非常不可能的。取決於您的設備,它可能低至16MB。此外,Android沒有壓縮GC算法,所以即使您的可用空間超過3MB,也可能沒有連續的3MB塊。

    這就是爲什麼它是要麼recycle()Bitmap對象重要或嘗試重用他們(例如,BitmapOptionsinBitmap,在API級別11)。

    此外,您可以使用DDMS創建一個堆轉儲和MAT來檢查它,以更準確地確定您的內存正在發生什麼,以及誰持有什麼。這在Android 3.0+上效果更好,因爲MAT將能夠更準確地在這些版本中報告Bitmap內存。

    是否只有一小部分的256 MB系統RAM在虛擬機崩潰之前實際提供給VM?

    是的。它被稱爲堆。Android設備具有堆大小限制。通常情況下,它在16-48MB範圍內,具體取決於Android操作系統版本和屏幕分辨率。

    難道是位圖加載過程有它自己的內存分配上限嗎?

    不,它使用相同的堆大小預算。從Android 3.0開始,它確實將內存從與其他Dalvik對象使用相同的堆中加載 - 以前,它使用堆外部的系統RAM塊,但是該空間是根據堆的大小預算計算的。

    但不知道到底是什麼原因引發的Dalvik GC我們仍然將非常多的信心在OS和谷歌的業績含糊討論最佳實踐

    生活,因爲他們說,那張。

    能否GC狀態(例如,「大約跑」,「跑」,「運行完畢」)從代碼追蹤,以便大資源分配可能會計劃圍繞戰略性可用內存? ...我想知道是否有支持的API調用可以依賴生產代碼(不僅僅是調試)來跟蹤垃圾收集器的準確狀態。

    是GC總是全系統的,或者可以單獨的線程(如遊戲專用渲染線程)逃脫引起GC的潛在性能滯後的問題?

    GC對於任何虛擬機都不是「系統範圍」的。 GC始終在VM內。

    在較新版本的Android上,GC是併發的,因此在正常情況下不會實質性阻塞任何線程。在較舊版本的Android上,GC會停下來,並會影響所有線程。對於Android 3.0來說,這個變化是明確的 - 我的記憶模糊了併發GC是否已經在Android 2.3版本中。 2011年有一篇關於Android內存管理的Google I | O介紹,您可能希望觀看。

    這會使VM崩潰嗎?或者有沒有某種方法可以自動處理這種極端情況以避免崩潰VM?

    Android應在提高OutOfMemoryException之前強制立即GC。根據我以前的paragaraph,這種情況不符合「正常情況」。

    +0

    感謝您的回覆!你知道JNI如何與Dalvik VM內存使用聯繫起來嗎?本機代碼應該管理自己的內存,但是內存是否與應用程序的堆空間預算相比較?如果是這樣,如果該線程嘗試分配太多或者會出現這種情況(最終/可能)導致系統崩潰,那麼可以使用本機線程分配的Android無力內存嗎? – CCJ

    +1

    @CCJ:「內存是根據應用程序的堆空間預算計算的嗎?」 - 如果它是真正的本地內存(例如'malloc()'),那麼我的理解是它不計入堆限制。但是,我不是JNI/NDK專家。 – CommonsWare

    +0

    嗯......好的,我必須進一步研究這個問題。謝謝! – CCJ