Замечания по Android GC - когда запускается GC и можно ли отслеживать его состояние из кода? - PullRequest
9 голосов
/ 14 декабря 2011

Некоторое время я искал подробные проектные документы, описывающие архитектуру сборщика мусора в виртуальной машине Dalvik, но пока ничего не нашел.Учитывая влияние на производительность прогонов GC, мне бы хотелось лучше понять 5 конкретных проблем: 1. Что именно вызывает GC в Android?Другие реализации виртуальных машин, которые я видел, обычно позволяют определенному проценту системной памяти выделяться приложению до того, как их 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 МБ.Это не имеет смысла для меня, так как GC недавно запустился и ничего не выделил, поскольку должен был вывести виртуальную машину в пределах 3 МБ емкости (256 МБ).Существует ли небольшой процент системного ОЗУ объемом 256 МБ, который фактически предоставляется виртуальной машине до ее сбоя?Может ли быть так, что у процесса загрузки растрового изображения есть свой предел выделения памяти?Я знаю, что объединение объектов - это хороший способ избежать GC во время игровых циклов, но, не зная ТОЧНО, что вызывает Dalvik GC, мы по-прежнему верим в ОС и смутные дискуссии Google о лучших практиках производительности.

Можно ли отслеживать состояние GC (например, «готов к выполнению», «выполняется», «завершено выполнение») из кода, чтобы можно было стратегически распределять большие ресурсы вокруг доступной памяти?Я прочитал этот пост по этому вопросу: Определите, когда Android GC запускает , который предлагает интересное потенциальное решение, но все еще полагается на «хитрость».Я хотел бы знать, есть ли где-нибудь поддерживаемый вызов API, на который можно положиться в производственном коде (а не только в отладке) для отслеживания точного состояния сборщика мусора.System.gc () может быть полезен в некоторых случаях, если можно проверить состояние GC;в противном случае, поскольку он не может обещать немедленный запуск GC, его полезность несколько падает.

Всегда ли GC общесистемный или может разделять потоки (например, выделенный поток рендеринга)для игры) избежать потенциальных проблем с производительностью, вызванных GC?

Учитывая следующий гипотетический сценарий: «У меня есть объект, стоимость которого (бюджет ОЗУ ВМ) / 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();
    }   
}
}

Tutorial View Manager Class:

/*
* 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

1 Ответ

23 голосов
/ 14 декабря 2011
  1. Что именно вызывает GC в Android?

Это внутренняя деталь реализации, о которой разработчики SDK не должны беспокоиться.

Другие реализации виртуальных машин, которые я видел, обычно позволяют определенному проценту системной памяти выделяться приложению до того, как их ГХ получает сигнал для запуска.

Я поверь на ваше слово. Ява не ведет себя так. JVM не заботится о том, сколько системной памяти существует - она ​​заботится только о своем потенциальном размере кучи (например, -Xmx) для своей собственной виртуальной машины, самое большее.

Сканирование следующего LogCat, однако, показывает, что Dalvik GC работает, по крайней мере частично, довольно часто

Правильно. В частности, в более новых версиях Android GC работает одновременно в своем собственном потоке, а не в подходе «остановка мира», принятом ранее.

Это не имеет смысла для меня, так как GC недавно запустился и ничего не выделил, поскольку должен был вывести виртуальную машину в пределах 3 МБ емкости (256 МБ).

Маловероятно, что у вас есть 256 МБ пространства кучи для вашей виртуальной машины. В зависимости от вашего устройства оно может составлять до 16 МБ.

Более того, Android не имеет алгоритма сжатия GC, и поэтому, даже если у вас доступно более 3 МБ, у вас может не быть непрерывного блока 3 МБ.

Вот почему важно либо recycle() ваших Bitmap объектов, либо пытаться использовать их повторно (например, inBitmap из BitmapOptions, добавлено в API уровня 11).

Кроме того, вы можете использовать DDMS для создания дампа кучи и MAT для его проверки, чтобы более точно определить, куда уходит ваша память и кто за что держится. Это лучше работает на Android 3.0+, так как в этих версиях MAT сможет точнее сообщать о Bitmap памяти.

Существует ли небольшой процент системного ОЗУ 256 МБ, который фактически передается виртуальной машине до ее сбоя?

Да. Это называется куча. Устройства Android имеют ограничение размера кучи. Как правило, он находится в диапазоне 16–48 МБ, в зависимости от версии ОС Android и разрешения экрана.

Может ли быть так, что процесс загрузки растрового изображения имеет свой собственный предел выделения памяти?

Нет, он работает на том же бюджете размера кучи. Начиная с Android 3.0, он действительно загружает память из той же кучи, что и остальные объекты Dalvik - ранее он использовал блоки системной оперативной памяти вне кучи, но пространство учитывалось по размеру бюджета кучи.

, но не зная ТОЧНО, что вызывает Dalvik GC, мы по-прежнему верим в ОС и смутные обсуждения Google о лучших практиках производительности

Жизнь, как говорится, продолжается.

Может ли состояние GC (например, «готов к выполнению», «выполняется», «завершено выполнение») отслеживаться из кода, чтобы можно было стратегически распределять большие ресурсы вокруг доступной памяти? ... Я хотел бы знать, есть ли где-нибудь поддерживаемый вызов API, на который можно опираться в производственном коде (а не только в отладке) для отслеживания точного состояния сборщика мусора.

номер

Является ли GC всегда общесистемным или отдельные потоки (например, выделенный поток рендеринга для игры) могут избежать потенциальных проблем с производительностью, вызванных GC?

GC никогда не является "общесистемным" для любой виртуальной машины. GC всегда находится внутри виртуальной машины.

В более новых версиях Android GC работает одновременно и, следовательно, в нормальных условиях не блокирует какие-либо потоки. В более старых версиях Android GC является "стоп-миром" и будет влиять на все потоки. Изменения определенно были в Android 3.0 - моя память неясна, был ли параллельный GC для Android 2.3 или нет. Вы можете посмотреть презентацию Google I | O 2011 года по управлению памятью в Android.

Это может привести к сбою виртуальной машины, или есть ли какая-то причина, по которой операционная система автоматически обрабатывает такие экстремальные ситуации, чтобы избежать сбоя виртуальной машины?

Android должен немедленно вызвать GC перед поднятием OutOfMemoryException.Этот сценарий квалифицируется как не «нормальные обстоятельства» для моего предыдущего парагарафа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...