Teknologian jakaminen

Android-suorituskyvyn optimointi, muistin optimointi

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Android-suorituskyvyn optimointi, muistin optimointi

muisti ongelma

  • muistin murskausta
  • muistivuoto
  • muistin ylivuoto

muistin murskausta

Muistin tuhoaminen tarkoittaa suuren määrän esineiden luomista ja tuhoamista lyhyessä ajassa, mikä johtaa usein tapahtuvaan roskien keräilyyn (Garbage Collection, GC). Tämä toistuva GC-toiminto vie paljon suoritinresursseja ja voi aiheuttaa sovelluksen viiveitä tai suorituskyvyn heikkenemistä.

Suorituskyky: Muistikäyrä on rosoinen.

muistivuoto

Muistivuoto tapahtuu, kun sovelluksessa on viittauksia objekteihin, joita ei enää tarvita, jolloin roskankerääjä ei pysty hyödyntämään näitä objekteja ja vievät näin muistitilaa, joka olisi voinut vapautua. Ajan myötä muistivuodot johtavat yhä vähemmän käytettävissä olevaan muistiin, mikä voi lopulta johtaa sovellusten kaatumiseen tai suorituskyvyn heikkenemiseen.

muistin ylivuoto

Muistin ylivuoto tapahtuu, kun sovellus yrittää varata lisää muistitilaa, mutta järjestelmä ei voi täyttää pyyntöä, koska varattavaa muistitilaa ei enää ole tarpeeksi. Tämä saa yleensä sovelluksen heittämään OutOfMemoryError-poikkeuksen.

Havaitsemistyökalu

  • Muistin profiloija
  • Muistin analysaattori
  • LeakCanary

Muistin profiloija

  • Memory Profiler on Android Studion mukana tuleva muistianalyysityökalu.
  • Reaaliaikainen kaavio, joka näyttää ohjelman muistin käytön.
  • Tunnista muistivuodot, lyönnit ja paljon muuta.
  • Tarjoaa mahdollisuuden kaapata kasavedoksia, pakottaa GC:tä ja seurata muistin varauksia.

Muistin analysaattori

  • Tehokas Java Heap -analyysityökalu muistivuotojen ja muistin käytön etsimiseen.
  • Luo yleisraportteja, analysoi ongelmia ja paljon muuta.

LeakCanary

  • Automaattinen muistivuotojen tunnistus.
    • LeakCanary havaitsee automaattisesti vuodot näissä kohteissa: Activity, Fragment, View, ViewModel, Service.
  • Virallinen verkkosivusto: https://github.com/square/leakcanary

Muistinhallintamekanismi

Java

Java-muistirakenne: pino, virtuaalikonepino, menetelmäalue, ohjelmalaskuri, paikallinen menetelmäpino.

Java-muistin kierrätysalgoritmi:

  • Merkitse ja pyyhkäise -algoritmi:
    1. Merkitse kierrätettävät esineet
    2. Kierrätä kaikki merkityt esineet tasaisesti.
  • Kopiointialgoritmi:
    1. Jaa muisti kahteen yhtä suureen osaan.
    2. Kun yksi muistilohko on käytetty loppuun, säilyneet objektit kopioidaan toiseen lohkoon.
  • Merkintä-lajittelualgoritmi:
    1. Merkintäprosessi on sama kuin "mark-and-sweep" -algoritmi.
    2. Selviytymisobjektit siirtyvät toiseen päähän.
    3. Tyhjennä jäljellä oleva muisti.
  • Sukupolvien keräysalgoritmi:
    • Yhdistä useiden keräysalgoritmien edut.
    • Uuden sukupolven objektien eloonjäämisaste on alhainen ja käytössä on kopiointialgoritmi.
    • Vanhan sukupolven esineiden eloonjäämisaste on korkea ja käytössä on mark-sort-algoritmi.

Merkintojen tyhjennys-algoritmin haitat: Merkintä ja tyhjennys eivät ole tehokkaita ja tuottavat suuren määrän epäjatkuvia muistin fragmentteja.

Replikointialgoritmi: yksinkertainen toteuttaa ja tehokas ajaa. Haitat: puolet tilasta hukkaan.

Mark-compact-algoritmi: vältä mark-cleanin aiheuttamaa muistin pirstoutumista ja vältä kopiointialgoritmin tilan tuhlaamista.

Android

Tietyt laitteet vaikuttavat Android-muistin elastiseen varaukseen, varausarvoon ja enimmäisarvoon.

Dalvikin kierrätysalgoritmi ja ART-kierrätysalgoritmi ovat molemmat roskankeräysmekanismeja, joita käytetään muistin hallintaan Android-käyttöjärjestelmässä.

Dalvikin kierrätysalgoritmi:

  • Merkitse ja pyyhkäise -algoritmi.
  • Etuna on, että se on helppo toteuttaa. Haittapuolena on, että sovelluksen suoritus keskeytyy merkintä- ja tyhjennysvaiheiden aikana, mikä saa sovelluksen väliaikaisesti jumittua ja vaikuttaa käyttökokemukseen.

Taiteen kierrätysalgoritmi:

  • Jätekeräyksen tiivistämisen algoritmi. Merkkipyyhkäisalgoritmiin perustuvia parannuksia on tehty taukoajan lyhentämiseksi roskien keräämisen aikana.
  • Samanaikainen merkintä: ART ottaa käyttöön samanaikaisen merkintävaiheen, mikä tarkoittaa, että se voi tapahtua samanaikaisesti sovelluksen suorittamisen kanssa. Tämä lyhentää roskien keräämisestä johtuvia taukoja.
  • Puhdistus ja tiivistäminen: Puhdistusvaiheen aikana ART ei ainoastaan ​​poista merkitsemättömiä objekteja, vaan myös tiivistää muistia, mikä tarkoittaa, että se siirtää säilyneet esineet yhteen muistin pirstoutumisen vähentämiseksi. Tämä tekee muistinhallinnasta tehokkaampaa ja vähentää muistin varaamisvirheiden mahdollisuutta.
  • Mukautuva keräys: ART esittelee myös mukautuvan keräyksen konseptin, mikä tarkoittaa, että se säätää automaattisesti roskien keräämisen tiheyttä ja tapaa sovelluksen käyttäytymisen ja muistin käyttötapojen perusteella. Tämän ansiosta ART mukautuu paremmin erilaisiin sovellusvaatimuksiin.

LMK-mekanismi:

  • Low Memory Killer -mekanismi.
  • Sen päätehtävä on lopettaa jotkin taustaprosessit tietyn prioriteettipolitiikan mukaisesti, kun järjestelmän muisti ei riitä vapauttamaan muistia ja varmistamaan järjestelmän vakauden ja reagoivuuden.

ratkaista

Muistin murtumisongelma

Simuloinnin ongelmakoodi
public class ShakeActivity extends AppCompatActivity {

    private static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            String str = "";
            for (int i = 0; i < 10000000; i++) {
                str += i;
            }
            mHandler.sendEmptyMessageDelayed(1, 30);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shake);
    }

    public void startClick(View view) {
        mHandler.sendEmptyMessage(1);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
Käytä Memory Profiler -työkalua tunnistamiseen

Memory Profiler voi tarkastella muistivarauksia napsauttamalla "Tallenna Java/Kotlin-varaukset".

Lisää kuvan kuvaus tähän

Yllä olevan merkitys:

  • Java: Java- tai Kotlin-koodin varaama muisti.
  • Alkuperäinen: C- tai C++-koodilla varattu muisti.
  • Grafiikka: Grafiikkapuskurijonon käyttämä muisti pikselien näyttämiseen näytöllä (mukaan lukien GL-pinnat, GL-tekstuurit jne.). CPU:n jaettu muisti.
  • Pino: sovelluksen alkuperäisen pinon ja Java-pinon käyttämä muisti. Tämä liittyy yleensä siihen, kuinka monta säiettä sovelluksesi on käynnissä.
  • Muisti, jota sovellukset käyttävät koodin ja resurssien, kuten dex-tavukoodin, optimoidun tai käännetyn indeksikoodin, .so-kirjastojen ja fonttien käsittelyyn.
  • Sovellus käyttää muistia, jonka luokittelusta järjestelmä ei ole varma.
  • Sovelluksen allokoimien Java/Kotlin-objektien määrä. Tämä numero ei ota huomioon C- tai C++-kielellä allokoituja objekteja.

Seuraavat merkitykset:

  • Määrärahat: Pass malloc() tainew Operaattorin osoittamien kohteiden määrä.
  • Varaukset: kautta free() taidelete Operaattorin jakamien kohteiden määrä.
  • Varausten koko: Kaikkien varausten kokonaiskoko valitulla ajanjaksolla tavuina.
  • Deallocations Size: Valitun ajanjakson aikana vapautetun muistin kokonaiskoko tavuina.
  • Kokonaismäärä: allokaatiot miinus jakaminen.
  • Jäljellä oleva koko: allokaatioiden koko miinus jakamisen koko.
  • Matala koko: Keon kaikkien esiintymien kokonaiskoko tavuina.

Ylläolevan kuvan analyysi:

Lisää kuvan kuvaus tähän

Varausten ja jakamisten arvot tällä alueella ovat suhteellisen samanlaisia, ja matala koko on suhteellisen suuri, mikä osoittaa, että esineitä voidaan luoda ja tuhota usein.

Lisää kuvan kuvaus tähän

Napsautuksen jälkeen voit tarkastella puhelupinon tietoja, ja yhdessä koodin kanssa voit päätellä, että Käsittelijässä on muistin värinäongelma.

Optimointivinkkejä
  • Vältä luomasta ja tuhoamasta esineitä usein.

Muistivuoto ongelma

Simuloinnin ongelmakoodi
public class CallbackManager {
    public static ArrayList<Callback> sCallbacks = new ArrayList<>();

    public static void addCallback(Callback callback) {
        sCallbacks.add(callback);
    }

    public static void removeCallback(Callback callback) {
        sCallbacks.remove(callback);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
public class LeakActivity extends AppCompatActivity implements Callback {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        ImageView imageView = findViewById(R.id.imageView);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
        imageView.setImageBitmap(bitmap);
        CallbackManager.addCallback(this);
    }

    @Override
    public void onOperate() {

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
Käytä LeakCanary-työkalua tunnistamiseen

Lisää riippuvaisia ​​kirjastoja:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
  • 1

Kun muistivuoto tapahtuu, LeakCanary luo asiaankuuluvat tiedot ja tyhjentää ne automaattisesti:

Lisää kuvan kuvaus tähän

Kuten yllä olevasta kuvasta voidaan nähdä: LeakActivityssä on muistivuoto, ja viiteketjun suhde näytetään.

Voit tietysti myös luoda hprof-tiedoston ja tarkastella tiettyjä tietoja Profiler-työkalun kautta:

Lisää kuvan kuvaus tähän

Kuten yllä olevasta kuvasta näkyy: 10 vuotokohtaa, mukaan lukien LeakActivity, voit tarkastella muistivuotoja ja tarkistaa viiteketjun.

Optimointivinkkejä
  • Keräyselementtien oikea-aikainen kierrätys.
  • Vältä staattisia viittauksia liian moneen tapaukseen.
  • Käytä staattisia sisäluokkia.
  • Sulje resurssiobjektit välittömästi.

Bittikartan optimointi

Jos et vapauta kuvaresursseja bittikartan käytön jälkeen, se on helppo aiheuttaaMuistivuoto, joka johtaa muistin ylivuotoon

Bittikarttamuistimalli
  • Ennen api10:tä (Android 2.3.3): Bittikarttaobjektit sijoitetaan kasomuistiin ja pikselitiedot paikalliseen muistiin.
  • api10:n jälkeen: kaikki kasamuistissa.
  • Api26:n (Android8.0) jälkeen: pikselitiedot tallennetaan paikalliseen muistiin. Tämä mahdollistaa alkuperäisen kerroksen bittikarttapikselitietojen nopean vapauttamisen yhdessä Java-kerroksen objektien kanssa.

Muistin kierrätys:

  • Ennen Android 3.0:aa sinun on kutsuttava se manuaalisestiBitmap.recycle()Suorita Bitmap-kierrätys.
  • Android 3.0:sta alkaen järjestelmä tarjoaa älykkäämmän muistinhallinnan, eikä useimmissa tapauksissa tarvitse manuaalisesti kierrättää Bitmapia.

Bittikarttapikselin kokoonpano:

KonfigVaratun tavun koko (tavu)havainnollistaa
ALPHA_81yksi läpinäkyvä kanava
RGB_5652Yksinkertainen RGB-sävy
ARGB_8888424-bittinen todellinen väri
RGBA_F168Android 8.0 New (HDR)

Laske Btimapin käyttämä muisti:

  • Bittikartta#getByteCount()
  • getWidth() * getHeight() * 1 pikseli vie muistia
Resurssitiedostohakemisto

Resurssitiedostoongelma:

  • mdpi (keskimääräinen tiheys): noin 160 dpi, 1x resurssi.
  • hdpi (suuri tiheys): noin 240 dpi, 1,5x resurssit.
  • xhdpi (Extra High Density): Noin 320 dpi, 2x resurssit.
  • xxhdpi (Extra Ultra High Density): Noin 480 dpi, 3x resurssit.
  • xxxhdpi (Extra Ultra High Density): Noin 640 dpi, 4x resurssit.

Testikoodi:

private void printBitmap(Bitmap bitmap, String drawable) {
    String builder = drawable +
            " Bitmap占用内存:" +
            bitmap.getByteCount() +
            " width:" +
            bitmap.getWidth() +
            " height:" +
            bitmap.getHeight() +
            " 1像素占用大小:" +
            getByteBy1px(bitmap.getConfig());
    Log.e("TAG", builder);
}

private int getByteBy1px(Bitmap.Config config) {
    if (Bitmap.Config.ALPHA_8.equals(config)) {
        return 1;
    } else if (Bitmap.Config.RGB_565.equals(config)) {
        return 2;
    } else if (Bitmap.Config.ARGB_8888.equals(config)) {
        return 4;
    }
    return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
// 逻辑密度
float density = metrics.density;
// 物理密度
int densityDpi = metrics.densityDpi;
Log.e("TAG", density + "-" + densityDpi);

// 1倍图
Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.splash1);
printBitmap(bitmap1, "drawable-mdpi");

// 2倍图
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.splash2);
printBitmap(bitmap2, "drawable-xhdpi");

// 3倍图
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.splash3);
printBitmap(bitmap3, "drawable-xxhdpi");

// 4倍图
Bitmap bitmap4 = BitmapFactory.decodeResource(getResources(), R.drawable.splash4);
printBitmap(bitmap4, "drawable-xxxhdpi");

// drawable
Bitmap bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.splash);
printBitmap(bitmap5, "drawable");
/*
        3.0-480
        drawable-mdpi Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
        drawable-xhdpi Bitmap占用内存:9281844 width:1287 height:1803 1像素占用大小:4
        drawable-xxhdpi Bitmap占用内存:4125264 width:858 height:1202 1像素占用大小:4
        drawable-xxxhdpi Bitmap占用内存:2323552 width:644 height:902 1像素占用大小:4
        drawable Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4
         */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

havainnollistaa:

1dp mdpi-laitteissa1px, 1dp xhdpi-laitteissa2px, 1dp==3px xxhdpi-laitteessa.

Siksi nykyinen laite on xxhdpi, joten saman kuvan leveys xxhdpi-resurssin alla on 858, mdpi-resurssin alla se suurennetaan 3-kertaiseksi ja leveys on 2574, ja xhdpi-resurssin alla 1,5-kertainen ja leveys on 1287.

Optimointivinkkejä
  • Määritä useita kuvaresursseja.
  • Valitse sopiva dekoodausmenetelmä.
  • Aseta kuvavälimuisti.

Lähdekoodin lataus