Ⅰ android 內存溢出怎麼解決
Android雖然會自動管理內存,java也有garbage collection (GC )內存回收機制。 但是如果程序在一次操作中打開幾個M的文件,那麼通常會出現下面的錯誤信息。 02-04 21:46:08.703: ERROR/dalvikvm-heap(2429): 1920000-byte external allocation too large for this process. 或 02-04 21:52:28.463: ERROR/AndroidRuntime(2429): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 移動終端因為內存有限,往往圖片處理經常出現上述的錯誤。 解決方法: 1.明確調用System.gc(); 這種內存回收會有一定的作用,但是請不要太期待。 2.圖片處理完成後回收內存。 請在調用BitMap進行圖片處理後進行內存回收。 bitmap.recycle(); 這樣會把剛剛用過的圖片佔用的內存釋放。 3.圖片處理時指定大小。 下面這個方法處理幾個M的圖片時是必須的 BitMap getBitpMap(){ ParcelFileDescriptor pfd; try{ pfd = mCon.getContentResolver().openFileDescriptor(uri, "r"); }catch (IOException ex){ return null; } java.io.FileDescriptor fd = pfd.getFileDescriptor(); BitmapFactory.Options options = new BitmapFactory.Options(); //先指定原始大小 options.inSampleSize = 1; //只進行大小判斷 options.inJustDecodeBounds = true; //調用此方法得到options得到圖片的大小 BitmapFactory.decodeFileDescriptor(fd, null, options); //我們的目標是在800pixel的畫面上顯示。 //所以需要調用computeSampleSize得到圖片縮放的比例 options.inSampleSize = computeSampleSize(options, 800); //OK,我們得到了縮放的比例,現在開始正式讀入BitMap數據 options.inJustDecodeBounds = false; options.inDither = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; //根據options參數,減少所需要的內存 Bitmap sourceBitmap = BitmapFactory.decodeFileDescriptor(fd, null, options); return sourceBitmap; } //這個函數會對圖片的大小進行判斷,並得到合適的縮放比例,比如2即1/2,3即1/3 static int computeSampleSize(BitmapFactory.Options options, int target) { int w = options.outWidth; int h = options.outHeight; int candidateW = w / target; int candidateH = h / target; int candidate = Math.max(candidateW, candidateH); if (candidate == 0) return 1; if (candidate > 1) { if ((w > target) && (w / candidate) < target) candidate -= 1; } if (candidate > 1) { if ((h > target) && (h / candidate) < target) candidate -= 1; } if (VERBOSE) Log.v(TAG, "for w/h " + w + "/" + h + " returning " + candidate + "(" + (w/candidate) + " / " + (h/candidate)); return candidate; }
麻煩採納,謝謝!
Ⅱ 如何檢查 Android 應用的內存使用情況
解析日誌信息
最簡單的調查應用內存使用情況的地方就是Dalvik日誌信息。可以在logcat(輸出信息可以在Device Monitor或者IDE中查看到,例如Eclipse和Android Studio)中找到這些日誌信息。每次有垃圾回收發生,logcat會列印出帶有下面信息的日誌消息:
Java
1
D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <External_memory_stats>, <Pause_time>
GC原因
觸發垃圾回收執行的原因和垃圾回收的類型。原因主要包括:
GC_CONCURRENT
並發垃圾回收,當堆開始填滿時觸發來釋放內存。
GC_FOR_MALLOC
堆已經滿了時應用再去嘗試分配內存觸發的垃圾回收,這時系統必須暫停應用運行來回收內存。
GC_HPROF_DUMP_HEAP
創建HPROF文件來分析應用時觸發的垃圾回收。
GC_EXPLICIT
顯式垃圾回收,例如當調用 gc()(應該避免手動調用而是要讓垃圾回收器在需要時主動調用)時會觸發。
GC_EXTERNAL_ALLOC
這種只會在API 10和更低的版本(新版本內存都只在Dalvik堆中分配)中會有。回收外部分配的內存(例如存儲在本地內存或NIO位元組緩沖區的像素數據)。
釋放數量
執行垃圾回收後內存釋放的數量。
堆狀態
空閑的百分比和(活動對象的數量)/(總的堆大小)。
外部內存狀態
API 10和更低版本中的外部分配的內存(分配的內存大小)/(回收發生時的限制值)。
暫停時間
越大的堆的暫停時間就越長。並發回收暫停時間分為兩部分:一部分在回收開始時,另一部分在回收將近結束時。
例如:
Java
1
D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/K, paused 2ms+2ms
隨著這些日誌消息的增多,注意堆狀態(上面例子中的3571K/9991K)的變化。如果值一直增大並且不會減小下來,那麼就可能有內存泄露了。
查看堆的更新
為了得到應用內存的使用類型和時間,可以在Device Monitor中實時查看應用堆的更新:
1.打開Device Monitor。
從<sdk>/tools/路徑下載入monitor工具。
2.在Debug Monitor窗口,從左邊的進程列表中選擇要查看的應用進程。
3.點擊進程列表上面的Update Heap。
4.在右側面板中選擇Heap標簽頁。
Heap視圖顯示了堆內存使用的基本狀況,每次垃圾回收後會更新。要看更新後的狀態,點擊Gause GC按鈕。
圖1.Device Monitor工具顯示[1] Update Heap和 [2] Cause GC按鈕。右邊的Heap標簽頁顯示堆的情況。
跟蹤內存分配
當要減少內存問題時,應該使用Allocation Tracker來更好的了解內存消耗大戶在哪分配。Allocation Tracker不僅在查看內存的具體使用上很有用,也可以分析應用中的關鍵代碼路徑,例如滑動。
例如,在應用中滑動列表時跟蹤內存分配,可以看到內存分配的動作,包括在哪些線程上分配和哪裡進行的分配。這對優化代碼路徑來減輕工作量和改善UI流暢性都極其有用。
使用Allocation Tracker:
1.打開Device Monitor 。
從<sdk>/tools/路徑下載入monitor工具。
2.在DDMS窗口,從左側面板選擇應用進程。
3.在右側面板中選擇Allocation Tracker標簽頁。
4.點擊Start Tracking。
5.執行應用到需要分析的代碼路徑處。
6.點擊Get Allocations來更新分配列表。
列表顯示了所有的當前分配和512大小限制的環形緩沖區的情況。點擊行可以查看分配的堆棧跟蹤信息。堆棧不只顯示了分配的對象類型,還顯示了屬於哪個線程哪個類哪個文件和哪一行。
圖2. Device Monitor工具顯示了在Allocation Tracker中當前應用的內存分配和堆棧跟蹤的情況。
注意:總會有一些分配是來自與 DdmVmInternal 和 allocation tracker本身。
盡管移除掉所有嚴重影響性能的代碼是不必要的(也是不可能的),但是allocation tracker還是可以幫助定位代碼中的嚴重問題。例如,應用可能在每個draw操作上創建新的Paint對象。把對象改成全局變數就是一個很簡單的改善性能的修改。
查看總體內存分配
為了進一步的分析,查看應用內存中不同內存類型的分配情況,可以使用下面的 adb 命令:
Java
1
adb shell mpsys meminfo <package_name>
應用當前的內存分配輸出列表,單位是千位元組。
當查看這些信息時,應當熟悉下面的分配類型:
私有(Clean and Dirty) 內存
進程獨占的內存。也就是應用進程銷毀時系統可以直接回收的內存容量。通常來說,「private dirty」內存是其最重要的部分,因為只被自己的進程使用。它只在內存中存儲,因此不能做分頁存儲到外存(Android不支持swap)。所有分配的Dalvik堆和本地堆都是「private dirty」內存;Dalvik堆和本地堆中和Zygote進程共享的部分是共享dirty內存。
實際使用內存 (PSS)
這是另一種應用內存使用的計算方式,把跨進程的共享頁也計算在內。任何獨占的內存頁直接計算它的PSS值,而和其它進程共享的頁則按照共享的比例計算PSS值。例如,在兩個進程間共享的頁,計算進每個進程PPS的值是它的一半大小。
PSS計算方式的一個好處是:把所有進程的PSS值加起來就可以確定所有進程總共佔用的內存。這意味著用PSS來計算進程的實際內存使用、進程間對比內存使用和總共剩餘內存大小是很好的方式。
例如,下面是平板設備中Gmail進程的輸出信息。它顯示了很多信息,但是具體要講解的是下面列出的一些關鍵信息。
注意:實際看到的信息可能和這里的稍有不同,輸出的詳細信息可能會根據平台版本的不同而不同。
Java
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
** MEMINFO in pid 9953 [com.google.android.gm] **
Pss Pss Shared Private Shared Private Heap Heap Heap
Total Clean Dirty Dirty Clean Clean Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------ ------
Native Heap 0 0 0 0 0 0 7800 7637(6) 126
Dalvik Heap 5110(3) 0 4136 4988(3) 0 0 9168 8958(6) 210
Dalvik Other 2850 0 2684 2772 0 0
Stack 36 0 8 36 0 0
Cursor 136 0 0 136 0 0
Ashmem 12 0 28 0 0 0
Other dev 380 0 24 376 0 4
.so mmap 5443(5) 1996 2584 2664(5) 5788 1996(5)
.apk mmap 235 32 0 0 1252 32
.ttf mmap 36 12 0 0 88 12
.dex mmap 3019(5) 2148 0 0 8936 2148(5)
Other mmap 107 0 8 8 324 68
Unknown 6994(4) 0 252 6992(4) 0 0
TOTAL 24358(1) 4188 9724 17972(2)16388 4260(2)16968 16595 336
Objects
Views: 426 ViewRootImpl: 3(8)
AppContexts: 6(7) Activities: 2(7)
Assets: 2 AssetManagers: 2
Local Binders: 64 Proxy Binders: 34
Death Recipients: 0
OpenSSL Sockets: 1
SQL
MEMORY_USED: 1739
PAGECACHE_OVERFLOW: 1164 MALLOC_SIZE: 62
通常來說,只需關心Pss Total列和Private Dirty列就可以了。在一些情況下,Private Clean列和Heap Alloc列也會提供很有用的信息。下面是一些應該查看的內存分配類型(行中列出的類型):
Dalvik Heap
應用中Dalvik分配使用的內存。Pss Total包含所有的Zygote分配(如上面PSS定義所描述的,共享跨進程的加權)。Private Dirty是應用堆獨占的內存大小,包含了獨自分配的部分和應用進程從Zygote復制分裂時被修改的Zygote分配的內存頁。
注意:新平台版本有Dalvik Other這一項。Dalvik Heap中的Pss Total和Private Dirty不包括Dalvik的開銷,例如即時編譯(JIT)和垃圾回收(GC),然而老版本都包含在Dalvik的開銷裡面。
Heap Alloc是應用中Dalvik堆和本地堆已經分配使用的大小。它的值比Pss Total和Private Dirty大,因為進程是從Zygote中復制分裂出來的,包含了進程共享的分配部分。
.so mmap和.dex mmap
mmap映射的.so(本地) 和.dex(Dalvik)代碼使用的內存。Pss Total 包含了跨應用共享的平台代碼;Private Clean是應用獨享的代碼。通常來說,實際映射的內存大小要大一點——這里顯示的內存大小是執行了當前操作後應用使用的內存大小。然而,.so mmap 的private dirty比較大,這是由於在載入到最終地址時已經為本地代碼分配好了內存空間。
Unknown
無法歸類到其它項的內存頁。目前,這主要包含大部分的本地分配,就是那些在工具收集數據時由於地址空間布局隨機化(Address Space Layout Randomization ,ASLR)不能被計算在內的部分。和Dalvik堆一樣, Unknown中的Pss Total把和Zygote共享的部分計算在內,Unknown中的Private Dirty只計算應用獨自使用的內存。
TOTAL
進程總使用的實際使用內存(PSS),是上面所有PSS項的總和。它表明了進程總的內存使用量,可以直接用來和其它進程或總的可以內存進行比較。
Private Dirty和Private Clean是進程獨自佔用的總內存,不會和其它進程共享。當進程銷毀時,它們(特別是Private Dirty)佔用的內存會重新釋放回系統。Dirty內存是已經被修改的內存頁,因此必須常駐內存(因為沒有swap);Clean內存是已經映射持久文件使用的內存頁(例如正在被執行的代碼),因此一段時間不使用的話就可以置換出去。
ViewRootImpl
進程中活動的根視圖的數量。每個根視圖與一個窗口關聯,因此可以幫助確定涉及對話框和窗口的內存泄露。
AppContexts和Activities
當前駐留在進程中的Context和Activity對象的數量。可以很快的確認常見的由於靜態引用而不能被垃圾回收的泄露的 Activity對象。這些對象通常有很多其它相關聯的分配,因此這是追查大的內存泄露的很好辦法。
注意:View 和 Drawable 對象也持有所在Activity的引用,因此,持有View 或 Drawable 對象也可能會導致應用Activity泄露。
獲取堆轉儲
堆轉儲是應用堆中所有對象的快照,以二進制文件HPROF的形式存儲。應用堆轉儲提供了應用堆的整體狀態,因此在查看堆更新的同時,可以跟蹤可能已經確認的問題。
檢索堆轉儲:
1.打開Device Monitor。
從<sdk>/tools/路徑下載入monitor工具。
2.在DDMS窗口,從左側面板選擇應用進程。
3.點擊Dump HPROF file,顯示見圖3。
4.在彈出的窗口中,命名HPROF文件,選擇存放位置,然後點擊Save。
圖3.Device Monitor工具顯示了[1] Dump HPROF file按鈕。
如果需要能更精確定位問題的堆轉儲,可以在應用代碼中調用mpHprofData()來生成堆轉儲。
堆轉儲的格式基本相同,但與Java HPROF文件不完全相同。Android堆轉儲的主要不同是由於很多的內存分配是在Zygote進程中。但是由於Zygote的內存分配是所有應用進程共享的,這些對分析應用堆沒什麼關系。
為了分析堆轉儲,你需要像jhat或Eclipse內存分析工具(MAT)一樣的標准工具。當然,第一步需要做的是把HPROF文件從Android的文件格式轉換成J2SE HRPOF的文件格式。可以使用<sdk>/platform-tools/路徑下的hprof-conv工具來轉換。hprof-conv的使用很簡單,只要帶上兩個參數就可以:原始的HPROF文件和轉換後的HPROF文件的存放位置。例如:
Java
1
hprof-conv heap-original.hprof heap-converted.hprof
注意:如果使用的是集成在Eclipse中的DDMS,那麼就不需要再執行HPROF轉換操作——默認已經轉換過了。
現在就可以在MAT中載入轉換過的HPROF文件了,或者是在可以解析J2SE HPROF格式的其它堆分析工具中載入。
分析應用堆時,應該查找由下導致的內存泄露:
對Activity、Context、View、Drawable的長期引用,以及其它可能持有Activity或Context容器引用的對象
非靜態內部類(例如持有Activity實例的Runnable)
不必要的長期持有對象的緩存
使用Eclipse內存分析工具
Eclipse內存分析工具(MAT)是一個可以分析堆轉儲的工具。它是一個功能相當強大的工具,功能遠遠超過這篇文檔的介紹,這里只是一些入門的介紹。
在MAT中打開類型轉換過的HPROF文件,在總覽界面會看到一張餅狀圖,它展示了佔用堆的最大對象。在圖表下面是幾個功能的鏈接:
Histogram view顯示所有類的列表和每個類有多少實例。
正常來說類的實例的數量應該是確定的,可以用這個視圖找到額外的類的實例。例如,一個常見的源碼泄露就是Activity類有額外的實例,而正確的是在同一時間應該只有一個實例。要找到特定類的實例,在列表頂部的<Regex>域中輸入類名查找。
當一個類有太多的實例時,右擊選擇List objects>with incoming references。在顯示的列表中,通過右擊選擇Path To GC Roots> exclude weak references來確定保留的實例。
Dominator tree是按照保留堆大小來顯示的對象列表。
應該注意的是那些保留的部分堆大小粗略等於通過GC logs、heap updates或allocation tracker觀察到的泄露大小的對象。
當看到可疑項時,右擊選擇Path To GC Roots>exclude weak references。打開新的標簽頁,標簽頁中列出了可疑泄露的對象的引用。
注意:在靠近餅狀圖中大塊堆的頂部,大部分應用會顯示Resources的實例,但這通常只是因為在應用使用了很多res/路徑下的資源。
圖4.MAT顯示了Histogram view和搜索」MainActivity」的結果。
想要獲得更多關於MAT的信息,請觀看2011年Google I/O大會的演講–《Android 應用內存管理》(Memory management for Android apps),在大約21:10 的時候有關於MAT的實戰演講。也可以參考文檔《Eclipse 內存分析文檔》(Eclipse Memory Analyzer documentation)。
對比堆轉儲
為了查看內存分配的變化,比較不同時間點應用的堆狀態是很有用的方法。對比兩個堆轉儲可以使用MAT:
1.按照上面描述得到兩個HPROF文件,具體查看獲取堆轉儲章節。
2.在MAT中打開第一個HPROF文件(File>Open Heap Dump)。
3.在Navigation History視圖(如果不可見,選擇Window>Navigation History),右擊Histogram,選擇Add to Comp are Basket。
4.打開第二個HRPOF文件,重復步驟2和3。
5.切換到Compare Basket視圖,點擊Compare the Results(在視圖右上角的紅色「!」圖標)。
觸發內存泄露
使用上述描述工具的同時,還應該對應用代碼做壓力測試來嘗試復現內存泄露。一個檢查應用潛在內存泄露的方法,就是在檢查堆之前先運行一會。泄露會慢慢達到分配堆的大小的上限值。當然,泄露越小,就要運行應用越長的時間來復現。
也可以使用下面的方法來觸發內存泄露:
1.在不同Activity狀態時,重復做橫豎屏切換操作。旋轉屏幕可能導致應用泄露 Activity、Context 或 View對象,因為系統會重新創建 Activity,如果應用在其它地方持有這些對象的引用,那麼系統就不能回收它們。
2.在不同Activity狀態時,做切換應用操作(切換到主屏幕,然後回到應用中)。
提示:也可以使用monkey測試來執行上述步驟。想要獲得更多運行 monkey 測試的信息,請查閱 monkeyrunner 文檔。
Ⅲ 如何用MAT分析Android應用內存泄露
1、新建測試應用
1
新建一個Android 測試應用。
填寫好應用的名稱,以及保存位置後,直接下一步到最後點擊「Finish」。
2
添加一個測試Activity:Activity2。
3
添加測試代碼ActivityHelper:
這里用常用的線程長時間執行,導致外部Activity Destroy時還持有 activity的內容導致內存泄露。
主要代碼如下:
public class ActivityHelper {
private Context mContext;
public ActivityHelper(Context context) {
this.mContext=context;
}
/** * 列印ActivityName */
public void printActivityName() {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true)
try {
Thread.sleep(1000*30);
Log.d(ActivityHelper.class.getSimpleName(), ((Activity) mContext).getClass().getSimpleName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}}
4
在Activity2中的onCreate方法中調用單例:
new ActivityHelper().printActivityName();
然後再MainActivity中點擊「Setting」菜單欄打開Activity2:
Intent intent=new Intent(MainActivity.this,Activity2.class);startActivity(intent);
最後按返回鍵返回。
按照以上操作,運行程序。
5
多次進入Activity2之後會發現內存一直在增長,並沒有降低。
而且log里會不停的輸出log,列印當前activity的name
END
2、抓取內存信息
1
點擊Android Studio工具欄上得「Android Device Monitor」,打開後如圖:
2
先在android設備上打開我們的應用
然後在devices里選中我們測試的應用,點擊「update heap」
3
來回點擊「settings」進入Activity2數次後,點擊
「DUMP HPROF file」保存文件。
4
轉換HPROF文件。
利用android sdk 的工具進行轉換:
到sdk/platform-tools/目錄下,打開終端輸入:
./hprof-conv
/Users/xxx/Desktop/gc/com.lulee007.androidmemoryleakdemo.hprof
/Users/xxx/Desktop/gc/com.lulee007.androidmemoryleakdemo.out.hprof
進行轉換.
最終得到這個文件:com.lulee007.androidmemoryleakdemo.out.hprof
END
3、使用Memory Analyzer 分析
下載Memory Analyzer 工具:
到官網下載:
選擇合適的版本。
打開軟體:
從工具欄打開phrof文件選擇「File」>>"open heap mp"
點擊下面的「Action」里的「Histogram」,然後搜索ActivityHelper
得到如下結果
右擊其中的一個,選擇「Merge Shortest Paths to GC Roots 」>>"exclude all phantom/weak/soft etc references.."
可以看到,ActivityHelper本應該被釋放,但是因為thead持有其中的mContext,而context是activity2里的,所有造成了內存泄露。
解決方法:
合理用thread,當activity2,destroy時,需要及時的停止掉thread。
可以設置activityhelper的while(true)改為設置變數,如while(notshutdown),
然後再destory里調用activityhelper,設置其notshutdown為false即可。
Ⅳ android手機測試中如何查看內存泄露
如何查看內存泄露
主要有2種方法
藉助工具,查看。
藉助adb 命令來查看。
【主要原理】
藉助工具來查看泄露的原因。
詳細實現方式
【工具查看】
常用工具有很多例如:
1.功能強大PC端檢測工具,如MemoryAnalyzer運行在PC端抓取Android手機中的mp文件進行深度分析。
2.小而優的Android端檢測工具,如LeakCanary隨App一起安裝會在Android手機桌面安裝的內存泄露檢測App
詳細的介紹網路裡面非常多這邊不做過多的介紹。
3.還有一種,要求不高的可以通過android studio查看內存變化等
詳細看到界面,視圖等佔用的內存情況。
【最後】
2種方法相互使用,驗證內存泄露原因。
Ⅳ 如何偷Android的內存
MemoryFile是android在最開始就引入的一套框架,其內部實際上是封裝了android特有的內存共享機制Ashmem匿名共享內存,簡單來說,Ashmem在Android內核中是被注冊成一個特殊的字元設備,Ashmem驅動通過在內核的一個自定義slab緩沖區中初始化一段內存區域,然後通過mmap把申請的內存映射到用戶的進程空間中(通過tmpfs),這樣子就可以在用戶進程中使用這里申請的內存了,另外,Ashmem的一個特性就是可以在系統內存不足的時候,回收掉被標記為」unpin」的內存,這個後面會講到,另外,MemoryFile也可以通過Binder跨進程調用來讓兩個進程共享一段內存區域。由於整個申請內存的過程並不再Java層上,可以很明顯的看出使用MemoryFile申請的內存實際上是並不會佔用Java堆內存的。
MemoryFile暴露出來的用戶介面可以說跟他的名字一樣,基本上跟我們平時的文件的讀寫基本一致,也可以使用InputStream和OutputStream來對其進行讀寫等操作:
1
2
3
4
MemoryFile memoryFile = new MemoryFile(null, inputStream.available());
memoryFile.allowPurging(false);
OutputStream outputStream = memoryFile.getOutputStream();
outputStream.write(1024);
上面可以看到allowPurging這個調用,這個就是之前說的」pin」和」unpin」,在設置了allowPurging為false之後,這個MemoryFile對應的Ashmem就會被標記成」pin」,那麼即使在android系統內存不足的時候,也不會對這段內存進行回收。另外,由於Ashmem默認都是」unpin」的,因此申請的內存在某個時間點內都可能會被回收掉,這個時候是不可以再讀寫了
Tricks
MemoryFile是一個非常trickly的東西,由於並不佔用Java堆內存,我們可以將一些對象用MemoryFile來保存起來避免GC,另外,這里可能android上有個BUG:
在4.4及其以上的系統中,如果在應用中使用了MemoryFile,那麼在mpsys meminfo的時候,可以看到多了一項Ashmem的值:
可以看出來雖然MemoryFile申請的內存不計入Java堆也不計入Native堆中,但是佔用了Ashmem的內存,這個實際上是算入了app當前佔用的內存當中
但是在4.4以下的機器中時,使用MemoryFile申請的內存居然是不算入app的內存中的:
而且這里我也算過,也是不算入Native Heap中的,另外,這個時候去系統設置裡面看進程的內存佔用,也可以看出來其實並沒有計入Ashmem的內存的
這個應該是android的一個BUG,但是我搜了一下並沒有搜到對應的issue,搞不好這里也可能是一個feature
而在大名鼎鼎的Fresco當中,他們也有用到這個bug來避免在decode bitmap的時候,將文件的位元組讀到Java堆中,使用了MemoryFile,並利用了這個BUG然這部分內存不算入app中,這里分別對應了Fresco中的GingerbreadPurgeableDecoder和KitKatPurgeableDecoder,Fresco在decode圖片的時候會在4.4和4.4以下的系統中分別使用這兩個不同的decoder
從這個地方可以看出來,使用MemoryFile,在4.4以下的系統當中,可以幫我們的app額外」偷」一些內存,並且可以不計入app的內存當中
Summary
這里主要是簡單介紹了MemoryFile的基本原理和用法,並且闡述了一個MemoryFile中一個可以幫助開發者」偷」內存的地方,這個是一個非常trickly的方法,雖然4.4以下使用這塊的內存並不計入進程當中,但是並不推薦大量使用,因為當設置了allowPurging為false的時候,這個對應的Ashmem內存區域是被」pin」了,那麼在android系統內存不足的時候,是不能夠把這段內存區域回收的,如果長時間沒有釋放的話,這樣子相當於無端端佔用了大量手機內存而又無法回收,那對系統的穩定性肯定會造成影響
Ⅵ Android獲取系統cpu信息,內存,版本,電量等信息
1、CPU頻率,CPU信息:/proc/cpuinfo和/proc/stat
通過讀取文件/proc/cpuinfo系統CPU的類型等多種信息。
讀取/proc/stat 所有CPU活動的信息來計算CPU使用率
下面我們就來講講如何通過代碼來獲取CPU頻率:
復制代碼 代碼如下:
package com.orange.cpu;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
public class CpuManager {
// 獲取CPU最大頻率(單位KHZ)
// "/system/bin/cat" 命令行
// "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" 存儲最大頻率的文件的.路徑
public static String getMaxCpuFreq() {
String result = "";
ProcessBuilder cmd;
try {
String[] args = { "/system/bin/cat",
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" };
cmd = new ProcessBuilder(args);
Process process = cmd.start();
InputStream in = process.getInputStream();
byte[] re = new byte[24];
while (in.read(re) != -1) {
result = result + new String(re);
}
in.close();
} catch (IOException ex) {
ex.printStackTrace();
result = "N/A";
}
return result.trim();
}
// 獲取CPU最小頻率(單位KHZ)
public static String getMinCpuFreq() {
String result = "";
ProcessBuilder cmd;
try {
String[] args = { "/system/bin/cat",
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq" };
cmd = new ProcessBuilder(args);
Process process = cmd.start();
InputStream in = process.getInputStream();
byte[] re = new byte[24];
while (in.read(re) != -1) {
result = result + new String(re);
}
in.close();
} catch (IOException ex) {
ex.printStackTrace();
result = "N/A";
}
return result.trim();
}
// 實時獲取CPU當前頻率(單位KHZ)
public static String getCurCpuFreq() {
String result = "N/A";
try {
FileReader fr = new FileReader(
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
BufferedReader br = new BufferedReader(fr);
String text = br.readLine();
result = text.trim();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
// 獲取CPU名字
public static String getCpuName() {
try {
FileReader fr = new FileReader("/proc/cpuinfo");
BufferedReader br = new BufferedReader(fr);
String text = br.readLine();
String[] array = text.split(":s+", 2);
for (int i = 0; i < array.length; i++) {
}
return array[1];
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
2、內存:/proc/meminfo
復制代碼 代碼如下:
public void getTotalMemory() {
String str1 = "/proc/meminfo";
String str2="";
try {
FileReader fr = new FileReader(str1);
BufferedReader localBufferedReader = new BufferedReader(fr, 8192);
while ((str2 = localBufferedReader.readLine()) != null) {
Log.i(TAG, "---" + str2);
}
} catch (IOException e) {
}
}
3、Rom大小
復制代碼 代碼如下:
public long[] getRomMemroy() {
long[] romInfo = new long[2];
//Total rom memory
romInfo[0] = getTotalInternalMemorySize();
//Available rom memory
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
romInfo[1] = blockSize * availableBlocks;
getVersion();
return romInfo;
}
public long getTotalInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
4、sdCard大小
復制代碼 代碼如下:
public long[] getSDCardMemory() {
long[] sdCardInfo=new long[2];
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
File sdcardDir = Environment.getExternalStorageDirectory();
StatFs sf = new StatFs(sdcardDir.getPath());
long bSize = sf.getBlockSize();
long bCount = sf.getBlockCount();
long availBlocks = sf.getAvailableBlocks();
sdCardInfo[0] = bSize * bCount;//總大小
sdCardInfo[1] = bSize * availBlocks;//可用大小
}
return sdCardInfo;
}
5、電池電量
復制代碼 代碼如下:
private BroadcastReceiver batteryReceiver=new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra("level", 0);
// level加%就是當前電量了
}
};
registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
6、系統的版本信息
復制代碼 代碼如下:
public String[] getVersion(){
String[] version={"null","null","null","null"};
String str1 = "/proc/version";
String str2;
String[] arrayOfString;
try {
FileReader localFileReader = new FileReader(str1);
BufferedReader localBufferedReader = new BufferedReader(
localFileReader, 8192);
str2 = localBufferedReader.readLine();
arrayOfString = str2.split("s+");
version[0]=arrayOfString[2];//KernelVersion
localBufferedReader.close();
} catch (IOException e) {
}
version[1] = Build.VERSION.RELEASE;// firmware version
version[2]=Build.MODEL;//model
version[3]=Build.DISPLAY;//system version
return version;
}
7、mac地址和開機時間
復制代碼 代碼如下:
public String[] getOtherInfo(){
String[] other={"null","null"};
WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if(wifiInfo.getMacAddress()!=null){
other[0]=wifiInfo.getMacAddress();
} else {
other[0] = "Fail";
}
other[1] = getTimes();
return other;
}
private String getTimes() {
long ut = SystemClock.elapsedRealtime() / 1000;
if (ut == 0) {
ut = 1;
}
int m = (int) ((ut / 60) % 60);
int h = (int) ((ut / 3600));
return h + " " + mContext.getString(R.string.info_times_hour) + m + " "
+ mContext.getString(R.string.info_times_minute);
}
Ⅶ android何時會發生oom怎麼解決oom
首先,OOM就是內存溢出,即Out Of Memory。也就是說內存佔有量超過了VM所分配的最大。
怎麼解決OOM,通常OOM都發生在需要用到大量內存的情況下(創建或解析Bitmap,分配特大的數組等),在這樣的一種情況下,就可能出現OOM,據我現在了解到,多數OOM都是因為Bitmap太大。所以,這里我就專門針對如何解決Bitmap的OOM。其實最核發的就是只載入可見范圍內的Bitmap,試想這樣一種情況,在GridView或ListView中,數據量有5000,每一屏只顯示20個元素,那麼不可見的,我們是不需要保存Bitmap在內在中的。所以我們就是只把那麼可見的Bitmap保留在內存中,那些不可見的,就釋放掉。當元素滑出來時,再去載入Bitmap。
這里我有兩種方式,都可以避免OOM。
Ⅷ 如何快速定位android app是否存在內存泄露
1、首先確定是否有內存泄露及哪個程序造成。
1.1、內存泄露已彈出out of memory對話框的情況。
這種情況很簡單,直接看對話框就知道是哪個應用的問題了。然後再分析該應用是否是因為內存泄露造成的
out of memory對話框。
》中介紹的各種方法進行分析,確定是否有內存泄露以及是哪個進程造成的內存泄露。
2、生成hprof文件,用MAT進行分析。
生成hprof文件可以在DDMS選中進程點擊窗口左上角的mp hprof file按鈕來直接生成,也可以通過在程序加代碼中來生成代碼2:voidgenerateHprof(){String packageName=getApplicationInfo().packageName;
StringhpFilePath=/data/data/+packageName+/input.hprof;try{//Debug.mpHprofData(/sdcard/input.hprof);Debug.
mpHprofData
(hpFilePath);}catch(IOException e) {//TODOAuto-generated catch block
e.printStackTrace();}}建議使用代碼生成hprof,然後使用《
Android內存泄露利器(hprof篇)》中的工具自動提取多個hprof文件,然後用MAT進行比較分析。在MAT導入.hprof文件以後,
MAT會自動解析並生成報告,點擊
Dominator Tree
,並按Package分組,選擇自己所定義的Package類,比較各個類在不同時期的RetainedHeap
,找出可疑類,然後選擇該類,點右鍵,選中
show retained Set項,參看Retained Heap
的詳細信息,進一步找出嫌疑項。
3、在代碼中查找內存泄露。
根據在MAT找到的內存泄露信息,參照《
Android內存泄漏簡介
》進一步在內存中查找內存泄露的原因並解決。
另外如果代碼很簡單,可以直接參照《
Android內存泄漏簡介
》在內存中查找內存泄露的原因並解決。
Ⅸ Android中如何獲得外置sd卡的路徑和手機自身內存的路徑
你問的編碼?
原文:http://blog.163.com/hero_213/blog/static/39891214201162123236660/
轉載非原創。
該代碼片段可以讓我們獲取internal和external的存儲空間大小。
import java.io.File;
import android.os.Environment;
import android.os.StatFs;
public class StorageUtil {
private static final int ERROR = -1;
/**
* SDCARD是否存
*/
public static boolean externalMemoryAvailable() {
return android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED);
}
/**
* 獲取手機內部剩餘存儲空間
* @return
*/
public static long () {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
}
/**
* 獲取手機內部總的存儲空間
* @return
*/
public static long getTotalInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
/**
* 獲取SDCARD剩餘存儲空間
* @return
*/
public static long () {
if (externalMemoryAvailable()) {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
} else {
return ERROR;
}
}
/**
* 獲取SDCARD總的存儲空間
* @return
*/
public static long getTotalExternalMemorySize() {
if (externalMemoryAvailable()) {
File path = Environment.getExternalStorageDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
} else {
return ERROR;
}
}
}
1.硬體上的 block size, 應該是"sector size",linux的扇區大小是512byte
2.有文件系統的分區的block size, 是"block size",大小不一,可以用工具查看
3.沒有文件系統的分區的block size,也叫「block size」,大小指的是1024 byte
4.Kernel buffer cache 的block size, 就是"block size",大部分PC是1024
5.磁碟分區的"cylinder size",用fdisk 可以查看。
我們這里的block size是第二種情況,一般SD卡都是fat32的文件系統,block size是4096.
這樣就可以知道手機的內部存儲空間和sd卡存儲空間的總大小和可用大小了。
Ⅹ android scrollview內存溢出怎麼解決
android scrollview內存溢出通常是由內存泄露導致。
1、內存泄露導致
由於我們程序的失誤,長期保持某些資源(如Context)的引用,垃圾回收器就無法回收它,當然該對象佔用的內存就無法被使用,這就造成內存泄露。
Android 中常見就是Activity被引用在調用finish之後卻沒有釋放,第二次打開activity又重新創建,這樣的內存泄露不斷的發生,則會導致內存的溢出。
Android的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,它是由Zygote服務進程孵化出來的,也就是說每個應用程序都是在屬於自己的進程中運行的。Android為不同類型的進程分配了不同的內存使用上限,如果程序在運行過程中出現了內存泄漏的而造成應用進程使用的內存超過了這個上限,則會被系統視為內存泄漏,從而被kill掉,這使得僅僅自己的進程被kill掉,而不會影響其他進程.
2、佔用內存較多的對象
保存了多個耗用內存過大的對象(如Bitmap)或載入單個超大的圖片,造成內存超出限制。
使用方法比較簡單:
· 選擇DDMS視圖,並打開Devices視圖和Heap視圖
· 點擊選擇要監控的進程,比如:上圖中我選擇的是system_process
· 選中Devices視圖界面上的"update heap" 圖標
· 點擊Heap視圖中的"Cause GC" 按鈕(相當於向虛擬機發送了一次GC請求的操作)
在Heap視圖中選擇想要監控的Type,一般我們會觀察dataobject的 total size的變化,正常情況下total size的值會穩定在一個有限的范圍內,也就說程序中的代碼良好,沒有造成程序中的對象不被回收的情況。如果代碼中存在沒有釋放對象引用的情況,那麼data object的total size在每次GC之後都不會有明顯的回落,隨著操作次數的增加而total size也在不斷的增加。(說明:選擇好data object後,不斷的操作應用,這樣才可以看出total size的變化)。如果totalsize確實是在不斷增加而沒有回落,說明程序中有沒有被釋放的資源引用。那麼我們應該怎麼來定位呢?
Android中內存泄露定位
通過DDMS工具可以判斷應用程序中是否存在內存泄漏的問題,那又如何定位到具體出現問題的代碼片段,最終找到問題所在呢?內存分析工具MAT Memory Analyzer Tool解決了這一難題。MAT工具是一個Eclipse 插件,同時也有單獨的RCP 客戶端,MAT工具的解析文件是.hprof,這個文件存放了某進程的內存快照。MAT工具定位內存泄漏具體位置的方法如下:
① 生成.hprof文件。Eclipse中生成.hprof文件的方法有很多,不同Android版本中生成.hprof的方式也稍有差別,但它們整體思路是一樣的。我們在DDMS界面選中想要分析的應用進程,在Devices視圖界面上方的一行圖標按鈕中,同時選中「Update Heap」和「Dump HPROF file」兩個按鈕,這時DDMS將會自動生成當前選中進程的.hprof文件。
② 將.hprof 文件導入到MAT工具中,MAT工具會自動解析並生成報告,點擊「Dominator Tree」按鈕,並按包分組,選擇已定義的包類點右鍵,在彈出的菜單中選擇List objects﹥With incoming references,這時會列出所有可疑的類。右鍵點擊某一項,並選擇Path to GC Roots﹥excludeweak/soft references,MAT工具會進一步篩選出跟程序相關的所有內存泄漏的類。這樣就可以追蹤到某一個產生內存泄漏的類的具體代碼中。
使用MAT內存分析工具查找內存泄漏的根本思路是找到哪個類的對象的引用沒有被釋放,然後分析沒有被釋放的原因,最終定位到代碼中哪些片段存在著內存泄漏。