起源
記憶體抖動是由於短時間內有大量物件進出新生區導致的,它伴隨著頻繁的GC。 gc會大量佔用ui執行緒和cpu資源,會導致app整體卡頓
android profile 效果圖如下圖
Memory 中
我們可以看到 上面的一溜白色垃圾桶。說明在大量的執行gc操作。用了一會兒 手機就開始卡了
學習內容
- android studio 3.0 編譯器 檢視記憶體抖動
- 使用工具來快速定位 引起記憶體抖動的程式碼。
- 學習 到什麼樣的 錯誤操作會導致記憶體都懂,如何避免。
快速定位記憶體抖動
快速定位 還得使用ddms。莫慌 as裡面自帶了 Tools->Android->Android Device Monitor 然後進行如下操作
然後我們看如下圖片。
不要慌。
中間紅框的就是我們要分析的內容,看他參差不齊的就是 記憶體抖動造成的。
然後我們把紅框 內容放大。滑鼠點住 然後往右拖動,就會變大,點選 紅框上面的數字就會變小。
我們將 抖動的地方 放大後。隨便點選會出現下圖樣式
可以看到這個粉色的拱門的 圖案。從它的左邊到右邊 代表 一個函式 消耗的時間。
我們接下來 就快速定位有問題的程式碼在哪裡
我就隨便的滑動了一下,然後 隨便的選中了一個, 然後下邊就展示了 我所選中的 函式方法。
這裡有一個細節
- onClick 最前面 的序號是 9
- Parent 下的方法 序號為8
- children 下的方法序號為10
說明 onClick 的序號 大於onClick 呼叫的方法 的序號。小於 onClick 被呼叫的方法的序號。
如果我們一直點選Parent 下的方法就會找到 序號為1 的方法
如下圖所示。
我們找到了錯誤程式碼在哪。那麼我們就看一下 原始碼的樣子
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imPrettySureSortingIsFree();
}
});
}
/**
*  排序後列印二維陣列,一行行列印
*/
public void imPrettySureSortingIsFree() {
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for (int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
for (int i = 0; i < lotsOfInts.length; i++) {
String rowAsStr = "";
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接列印
for (int j = 0; j < lotsOfInts[i].length; j++) {
rowAsStr += sorted[j];
if (j < (lotsOfInts[i].length - 1)) {
rowAsStr += ", ";
}
}
}
}
public int[] getSorted(int[] input) {
int[] clone = input.clone();
Arrays.sort(clone);
return clone;
}
}
複製程式碼
發現 rowAsStr 物件在被不斷地建立。 我們可以把它優化一下
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imPrettySureSortingIsFree();
}
});
}
/**
*  排序後列印二維陣列,一行行列印
*/
public void imPrettySureSortingIsFree() {
int dimension = 300;
int[][] lotsOfInts = new int[dimension][dimension];
Random randomGenerator = new Random();
for (int i = 0; i < lotsOfInts.length; i++) {
for (int j = 0; j < lotsOfInts[i].length; j++) {
lotsOfInts[i][j] = randomGenerator.nextInt();
}
}
//優化以後
StringBuilder sb = new StringBuilder();
String rowAsStr = "";
for(int i = 0; i < lotsOfInts.length; i++) {
//清除上一行
sb.delete(0,rowAsStr.length());
//排序
int[] sorted = getSorted(lotsOfInts[i]);
//拼接列印
for (int j = 0; j < lotsOfInts[i].length; j++) {
// rowAsStr += sorted[j];
sb.append(sorted[j]);
if(j < (lotsOfInts[i].length - 1)){
// rowAsStr += ", ";
sb.append(", ");
}
}
rowAsStr = sb.toString();
Log.i("ricky", "Row " + i + ": " + rowAsStr);
}
}
public int[] getSorted(int[] input) {
int[] clone = input.clone();
Arrays.sort(clone);
return clone;
}
}
複製程式碼
這樣就不會記憶體抖動了
能找到問題,這個問題就基本解決了。
下面是避免發生記憶體抖動的幾點建議:
- 儘量避免在迴圈體內建立物件,應該把物件建立移到迴圈體外。
- 注意自定義View的onDraw()方法會被頻繁呼叫,所以在這裡面不應該頻繁的建立物件。
- 當需要大量使用Bitmap的時候,試著把它們快取在陣列中實現複用。
- 對於能夠複用的物件,同理可以使用物件池將它們快取起來。
其他
分析皮膚 皮膚列名含義如下:
Name | 方法的詳細資訊,包括包名和引數資訊 |
---|---|
col 3 is | right-aligned |
col 2 is | centered |
zebra stripes | are neat |
Incl Cpu Time | Cpu執行該方法該方法及其子方法所花費的時間 |
Incl Cpu Time % | Cpu執行該方法該方法及其子方法所花費佔Cpu總執行時間的百分比 |
Excl Cpu Time | Cpu執行該方法所話費的時間 |
Excl Cpu Time % | Cpu執行該方法所話費的時間佔Cpu總時間的百分比 |
Incl Real Time | 該方法及其子方法執行所話費的實際時間,從執行該方法到結束一共花了多少時間 |
Incl Real Time % | 上述時間佔總的執行時間的百分比 |
Excl Real Time % | 該方法自身的實際允許時間 |
Excl Real Time | 上述時間佔總的允許時間的百分比 |
Calls+Recur | 呼叫次數+遞迴次數,只在方法中顯示,在子展開後的父類和子類方法這一欄被下面的資料代替 |
Calls/Total | 呼叫次數和總次數的佔比 |
Cpu Time/Call | Cpu執行時間和呼叫次數的百分比,代表該函式消耗cpu的平均時間 |
Real Time/Call | 實際時間於呼叫次數的百分比,該表該函式平均執行時間 |
參考
https://www.youtube.com/watch?v=McAvq5SkeTk