亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android開發筆記——常見BUG類型之內存泄露與線程安全

發布時間:2020-06-13 16:19:15 來源:網絡 閱讀:638 作者:xsster 欄目:移動開發

一、內存泄露

1、很抱歉,”XXX”已停止運行。OOM?

Android開發筆記——常見BUG類型之內存泄露與線程安全

怎樣才能讓app報OOM呢?最簡單的辦法如下:

Bitmap bt1 = BitmapFactory.decodeResource(this.getResources(), R.drawable.p_w_picpath);
Bitmap bt2 = BitmapFactory.decodeResource(this.getResources(), R.drawable.p_w_picpath);
Bitmap btn = ...
 
2、查看內存占用
  • 命令行:adb shell dumpsys meminfo packageName

Android開發筆記——常見BUG類型之內存泄露與線程安全

  • 通過Android Studio的Memory Monitor查看內存中Dalvik Heap的實時變化

Android開發筆記——常見BUG類型之內存泄露與線程安全

 
3、發生內存泄露的條件

首先,每個app有最大內存限制。

ActivityManager activityManager = (ActivityManager) context.getSystemServiceContext.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
 
getMemoryClass()取到的是最大內存資源。Android中的堆內存分為Native Heap和Dalvik Heap。C/C++申請的內存空間在Native Heap中,Java申請的內存空間則在Dalvik Heap中。對于head堆的大小限制,可以查看/system/build.prop文件:
 
dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=96m
dalvik.vm.heapsize=256m
注意:

heapsize參數表示單個進程heap可用的最大內存,但如果存在以下參數”dalvik.vm.headgrowthlimit =96m”表示單個進程heap內存被限定在96m,即程序運行過程實際只能使用96m內存。

如果申請的內存資源超過上述限制,系統就會拋出OOM錯誤。

 

4、常見避免OOM的措施

以下主要從四個方面總結常見的措施:1)減小對象的內存占用;2)內存對象的重復利用;3)避免對象的內存泄露;4)內存使用策略優化。

4.1 減小對象的內存占用

  • 使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。

    • 參考鏈接:Android內存優化(使用SparseArray和ArrayMap代替HashMap)

  • 在Android中避免使用枚舉。

  • 減小Bitmap對象的內存占用。inSampleSize和decode format。

4.2 內存對象的重復利用

  • ListView/GridView等出現大量重復子組件的視圖里面對ConvertView的復用

  • 使用LRU機制緩存Bitmap

  • 避免在onDraw方法里面執行對象的創建

  • 使用StringBuilder來替代頻繁的”+”

4.3 避免對象的內存泄露

4.1和4.2都是比較常規的措施,4.3需要重點關注。

1)Activity泄露

導致Activity泄露的原因較多,下面列舉一些比較常見的。從原理上主要分為兩類:i)靜態對象;ii)this$0

  • Activity被static變量引用。這段代碼來自于我們的Crash上傳

    Android開發筆記——常見BUG類型之內存泄露與線程安全

    private static Map<ComponentName, ExceptionHandler> configMap = 
                            new HashMap<ComponentName, ExceptionHandler>();public static void setActivity(final Activity activity, boolean send2Server) {
        Log.d(TAG, "bind exception handler : " + activity.getComponentName().getClassName());    //上下文初始化    SDKContext.init(activity.getApplication());
        init(activity.getApplication());
        ExceptionHandler exceptionHandler = new ExceptionHandler(
                            activity, send2Server, Thread.getDefaultUncaughtExceptionHandler());
        configMap.put(activity.getComponentName(), exceptionHandler);
        Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
    }

    Android開發筆記——常見BUG類型之內存泄露與線程安全

下面是通過MAT分析一個Activity泄露的截圖:
 Android開發筆記——常見BUG類型之內存泄露與線程安全

  • 內部類引用導致Activity的泄漏
    最典型的場景是Handler導致的Activity泄漏,如果Handler中有延遲的任務或者是等待執行的任務隊列過長,都有可能因為Handler繼續執行而導致Activity發生泄漏。此時的引用關系鏈是Looper -> MessageQueue -> Message -> Handler -> Activity。為了解決這個問題,可以在UI退出之前,執行remove Handler消息隊列中的消息與runnable對象。或者是使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關系的目的。
    可參考鏈接:線程通信

2)考慮使用Application Context而不是Activity Context

對于大部分非必須使用Activity Context的情況(Dialog的Context就必須是Activity Context),我們都可以考慮使用Application Context而不是Activity的Context,這樣可以避免不經意的Activity泄露。

3)注意臨時Bitmap對象的及時回收

雖然在大多數情況下,我們會對Bitmap增加緩存機制,但是在某些時候,部分Bitmap是需要及時回收的。例如臨時創建的某個相對比較大的bitmap對象,在經過變換得到新的bitmap對象之后,應該盡快回收原始的bitmap,這樣能夠更快釋放原始bitmap所占用的空間。

4)內存占用監控
通過Runtime獲取maxMemory,而maxMemory-totalMemory即為剩余可使用的dalvik內存。定期檢查這個值,達到80%就去釋放各種cache資源(bitmap的cache)。

Android開發筆記——常見BUG類型之內存泄露與線程安全

/**
 * Returns the maximum number of bytes the heap can expand to. See {@link #totalMemory} for the
 * current number of bytes taken by the heap, and {@link #freeMemory} for the current number of
 * those bytes actually used by live objects. */int maxMemory = Runtime.getRuntime().maxMemory()); // 應用程序最大可用內存/**
 * Returns the number of bytes taken by the heap at its current size. The heap may expand or
 * contract over time, as the number of live objects increases or decreases. See
 * {@link #maxMemory} for the maximum heap size, and {@link #freeMemory} for an idea of how much
 * the heap could currently contract. */long totalMemory = Runtime.getRuntime().totalMemory()); // 應用程序已獲得內存/**
 * Returns the number of bytes currently available on the heap without expanding the heap. See
 * {@link #totalMemory} for the heap's current size. When these bytes are exhausted, the heap
 * may expand. See {@link #maxMemory} for that limit. */long freeMemory = Runtime.getRuntime().freeMemory()); // 應用程序已獲得內存中未使用內存

Android開發筆記——常見BUG類型之內存泄露與線程安全


5)注意Cursor對象是否及時關閉

在程序中我們經常會進行查詢數據庫的操作,但時常會存在不小心使用Cursor之后沒有及時關閉的情況。這些Cursor的泄露,反復多次出現的話會對內存管理產生很大的負面影響,我們需要謹記對Cursor對象的及時關閉。

4.4 內存使用策略優化

  • 謹慎使用large heap

  • 綜合考慮設備內存閾值與其他因素設計合適的緩存大小

  • onLowMemory()/onTrimMemory(int)

  • 資源文件需要選擇合適的文件夾進行存放

  • Try catch某些大內存分配的操作

  • 謹慎使用static對象

  • 優化布局層次,減少內存消耗

  • 謹慎使用多進程

  • 謹慎使用依賴注入框架

  • 使用ProGuard來剔除不需要的代碼

  • 謹慎使用第三方libraries

  • 考慮不同的實現方式來優化內存占用

二、線程安全

1、下面的方法是線程安全的嗎?
怎樣使上述方法線程安全?
 
2、Java中的線程安全

怎樣保持在多線程環境下的數據一致性,Java提供了多種方法實現:

  1. synchronized

  2. java.util.concurrent.atomic

  3. java.util.concurrent.locks

  4. thread safe collection(ConcurrentHashMap)

  5. volatile

2.1 synchronized

JVM保證被synchronized關鍵字修飾的代碼段在同一時間只能被一個線程訪問,內部通過對對象加鎖來實現的。當方法被synchronized修飾時,鎖加在對象上;當方法同時為static時,鎖加在類上。從性能的角度來講,一般不建議直接將鎖加在類上,這樣會使得類的所有對象的該方法均為synchronized的。

從之前掃描的問題來看,在編寫synchronized程序時主要有兩點需要注意:

  • synchronized需要創建基于對象或者類的鎖,所以不能在構造器或者變量上加鎖。

  • synchronized造成死鎖。

1) 鎖加在哪里?

Android開發筆記——常見BUG類型之內存泄露與線程安全

List<ResultPoint> currentPossible = possibleResultPoints;
List<ResultPoint> currentLast = lastPossibleResultPoints;int frameLeft = frame.left;int frameTop = frame.top;if (currentPossible.isEmpty()) {
    lastPossibleResultPoints = null;
} else {
    possibleResultPoints = new ArrayList<>(5);
    lastPossibleResultPoints = currentPossible;
    paint.setAlpha(CURRENT_POINT_OPACITY);
    paint.setColor(resultPointColor);    synchronized (currentPossible) {        for (ResultPoint point : currentPossible) {
            canvas.drawCircle(frameLeft                    + (int) (point.getX() * scaleX), frameTop                    + (int) (point.getY() * scaleY), POINT_SIZE,
                    paint);
        }
    }
}

Android開發筆記——常見BUG類型之內存泄露與線程安全

上述方法中,possibleResultPoints的創建沒有采用同步措施,需要使用Collections.synchronizedXxx

List<MyType> list = Collections.synchronizedList(new ArrayList(<MyType>));
...synchronized(list){    for(MyType m : list){
        foo(m);
        m.doSomething();
    }
}
一般比較推薦創建一個虛擬的對象專門用于獲取鎖。

Android開發筆記——常見BUG類型之內存泄露與線程安全

//dummy object variable for synchronizationprivate Object mutex=new Object();
...//using synchronized block to read, increment and update count value synchronouslysynchronized (mutex) {
        count++;
}

Android開發筆記——常見BUG類型之內存泄露與線程安全


PS:直接在方法上加synchronized可能DoS***喔,舉個栗子:

Android開發筆記——常見BUG類型之內存泄露與線程安全

public class MyObject {    // Locks on the object's monitor
    public synchronized void doSomething() { 
    // ...    }
}// ***的代碼MyObject myObject = new MyObject();synchronized (myObject) {    while (true) {        // Indefinitely delay myObject        Thread.sleep(Integer.MAX_VALUE); 
    }
}

Android開發筆記——常見BUG類型之內存泄露與線程安全

***的代碼獲取了MyObject對象的鎖,導致doSomething死鎖,從而引發Denial of Service。

Android開發筆記——常見BUG類型之內存泄露與線程安全

public class MyObject {    //locks on the class object's monitor
    public static synchronized void doSomething() { 
    // ...    }
}// ***的代碼synchronized (MyObject.class) {    while (true) {
        Thread.sleep(Integer.MAX_VALUE); // Indefinitely delay MyObject    }
}

Android開發筆記——常見BUG類型之內存泄露與線程安全

2) 死鎖。

Android開發筆記——常見BUG類型之內存泄露與線程安全

public class ThreadDeadlock {    public static void main(String[] args) throws InterruptedException {
        Object obj1 = new Object();
        Object obj2 = new Object();
        Object obj3 = new Object();
        Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
        Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
        Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");
        t1.start();
        Thread.sleep(5000);
        t2.start();
        Thread.sleep(5000);
        t3.start();
    }
}class SyncThread implements Runnable{    private Object obj1;    private Object obj2;    public SyncThread(Object o1, Object o2){        this.obj1=o1;        this.obj2=o2;
    }
    @Override    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on "+obj1);        synchronized (obj1) {
         System.out.println(name + " acquired lock on "+obj1);
         work();
         System.out.println(name + " acquiring lock on "+obj2);         synchronized (obj2) {
            System.out.println(name + " acquired lock on "+obj2);
            work();
        }
         System.out.println(name + " released lock on "+obj2);
        }
        System.out.println(name + " released lock on "+obj1);
        System.out.println(name + " finished execution.");
    }    private void work() {        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Android開發筆記——常見BUG類型之內存泄露與線程安全


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大埔县| 兴海县| 和政县| 大埔区| 木兰县| 比如县| 横山县| 芷江| 从江县| 石首市| 扎兰屯市| 营口市| 崇仁县| 宁国市| 广汉市| 盐山县| 确山县| 嘉义市| 崇州市| 宜昌市| 罗源县| 新安县| 昌都县| 北京市| 嘉祥县| 云阳县| 新安县| 娱乐| 宾阳县| 临沧市| 益阳市| 海城市| 东光县| 博野县| 民县| 萍乡市| 东源县| 阿拉善左旗| 钟山县| 金湖县| 民丰县|