A. 如何解決android studio內存不夠用的問題
Android studio默認最大內存是750M,這樣跑起來非常的卡。
這個默認值是可以修改的,
在android studio目錄下找到:studio64.exe.vmoptions文件,
綠色部分為修改的參數(-Xmx1050m),將默認參數修改為1050MB,這樣跑起來就非常流暢了,
如果覺得還是不夠流暢,可以改得更高:
-Xms128m
-Xmx1050m
-XX:MaxPermSize=350m
-XX:ReservedCodeCacheSize=96m
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djna.nosys=true
-Djna.boot.library.path=
-Djna.debug_load=true
-Djna.debug_load.jna=true
-Djsse.enableSNIExtension=false
-XX:+UseCodeCacheFlushing
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-Didea.platform.prefix=AndroidStudio
-Didea.paths.selector=AndroidStudio
B. 如何檢查 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 文檔。
C. 如何讓Android應用一直活著,確保佔用系統資源
java中的內存溢出和內存泄漏 內存溢出: 對於整個應用程序來說,JVM內存空間,已經沒有多餘的空間分配給新的對象。所以就發生內存溢出。 內存泄露: 在應用的整個生命周期內,某個對象一直存在,且對象佔用的內存空間越來越大,最終導致JVM內存泄露, 比如:緩存的應用,如果不設置上限的話,緩存的容量可能會一直增長。 靜態集合引用,如果該集合存放了無數個對象,隨著時間的推移也有可能使容量無限制的增長,最終導致JVM內存泄露。 內存泄露,是應用程序中的某個對象長時間的存活,並且佔用空間不斷增長,最終導致內存泄露。 是對象分配後,長時間的容量增長。 內存溢出,是針對整個應用程序的所有對象的分配空間不足,會造成內存溢出。 內存泄漏 內存泄漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設 計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。內存泄漏與許多其他問題有著相似的症狀,並且通常情況下只能由那些可以獲得程序源代碼的程序員才 可以分析出來。然而,有不少人習慣於把任何不需要的內存使用的增加描述為內存泄漏,即使嚴格意義上來說這是不準確的。 一般我們常說的內存泄漏 是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完後必須顯示釋放的內存。應用程序一般使用 malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內 存就不能被再次使用,我們就說這塊內存泄漏了。 內存泄漏可以分為4類: 1. 常發性內存泄漏。發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。 2. 偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。 3. 一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存泄漏只會發生一次。 4. 隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里並沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但 是對於一個伺服器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。 簡單點: 內存泄漏就是忘記釋放使用完畢的內存,讓下次使用有一定風險。 內存溢出就是一定的內存空間不能裝下所有的需要存放的數據,造成內存數據溢出。 主要從以下幾部分來說明,關於內存和內存泄露、溢出的概念,區分內存泄露和內存溢出;內存的區域劃分,了解GC回收機制;重點關注如何去監控和發現內存問題;此外分析出問題還要如何解決內存問題。 下面就開始本篇的內容: 第一部分 概念 眾所周知,java中的內存由java虛擬機自己去管理的,他不像C++需要自己去釋放。籠統地 去講,java的內存分配分為兩個部分,一個是數據堆,一個是棧。程序在運行的時候一般分配數據堆,把局部的臨時的變數都放進去,生命周期和進程有關系。 但是如果程序員聲明了static的變數,就直接在棧中運行的,進程銷毀了,不一定會銷毀static變數。 另外為了保證java內存不會溢出,java中有垃圾回收機制。 System.gc()即垃圾收集機制是指jvm用於釋放那些不再使用的對象所佔用的內存。java語言並不要求jvm有gc,也沒有規定gc如何工作。垃圾收集的目的在於清除不再使用的對象。gc通過確定對象是否被活動對象引用來確定是否收集該對象。 而其中,內存溢出就是你要求分配的java虛擬機內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。 內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪 問,該塊已分配出來的內存也無法再使用,隨著伺服器內存的不斷消耗,而無法使用的內存越來越多,系統也不能再次將它分配給需要的程序,產生泄露。一直下 去,程序也逐漸無內存使用,就會溢出。 第二部分 原理 JAVA垃圾回收及對內存區劃分 在Java虛擬機規范中,提及了如下幾種類型的內存空間: ◇ 棧內存(Stack):每個線程私有的。 ◇ 堆內存(Heap):所有線程公用的。 ◇ 方法區(Method Area):有點像以前常說的「進程代碼段」,這裡面存放了每個載入類的反射信息、類函數的代碼、編譯時常量等信息。 ◇ 原生方法棧(Native Method Stack):主要用於JNI中的原生代碼,平時很少涉及。 而Java的使用的是堆內存,java堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象,「垃圾回收」也是主要是和堆內存(Heap)有關。 垃圾回收的概念就是JAVA虛擬機(JVM)回收那些不再被引用的對象內存的過程。一般我們認為正在被引用的對象狀態為「alive」,而沒有 被應用或者取不到引用屬性的對象狀態為「dead」。垃圾回收是一個釋放處於」dead」狀態的對象的內存的過程。而垃圾回收的規則和演算法被動態的作用於 應用運行當中,自動回收。 JVM的垃圾回收器採用的是一種分代(generational )回收策略,用較高的頻率對年輕的對象(young generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱為major collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍,這種策略有利於實時觀察和回收。 (Sun JVM 1.3 有兩種最基本的內存收集方式:一種稱為ing或scavenge,將所有仍然生存的對象搬到另外一塊內存後,整塊內存就可回收。這種方法有效率,但需要有一定的空閑內存,拷貝也有開銷。這種方法用於minor collection。另外一種稱為mark-compact,將活著的對象標記出來,然後搬遷到一起連成大塊的內存,其他內存就可以回收了。這種方法不需要佔用額外的空間,但速度相對慢一些。這種方法用於major collection. ) 一些對象被創建出來只是擁有短暫的生命周期,比如 iterators 和本地變數。另外一些對象被創建是擁有很長的生命周期,比如持久化對象等。 垃圾回收器的分代策略是把內存區劃分為幾個代,然後為每個代分配一到多個內存區塊。當其中一個代用完了分配給他的內存後,JVM會在分配的內存區內執行一個局部的GC(也可以叫minor collection)操作,為了回收處於「dead」狀態的對象所佔用的內存。局部GC通常要比Full GC快很多。 JVM定義了兩個代,年輕代(yong generation)(有時稱為「nursery」托兒所)和老年代(old generation)。年輕代包括 「Eden space(伊甸園)」和兩個「survivor spaces」。虛擬內存初始化的時候會把所有對象都分配到 Eden space,並且大部分對象也會在該區域被釋放。 當進行 minor GC的時候,VM會把剩下的沒有釋放的對象從Eden space移動到其中一個survivor spaces當中。此外,VM也會把那些長期存活在survivor spaces 里的對象移動到 老生代的「tenured」 space中。當 tenured generation 被填滿後,就會產生Full GC,Full GC會相對比較慢因為回收的內容包括了所有的 live狀態的對象。pemanet generation這個代包括了所有java虛擬機自身使用的相對比較穩定的數據對象,比如類和對象方法等。 關於代的劃分,可以從下圖中獲得一個概況: 第三部分 總結 內存溢出主要是由於代碼編寫時對某些方法、類應用不合理,或者沒有預估到臨時對象會佔用很大內存量,或者把過多的數據放入JVM緩存,或者性能 壓力大導致消息堆積而佔用內存,以至於在性能測試時,生成龐大數量的臨時對象,GC時沒有做出有效回收甚至根本就不能回收,造成內存空間不足,內存溢出。 如果編碼之前,對內存使用量進行預估,對放在內存中的數據進行評估,保證有用的信息盡快釋放,無用的信息能夠被GC回收,這樣在一定程度上是可以避免內存溢出問題的。
D. Java 棧內存和堆內存的分配
1.我認為棧用來存儲臨時變數的。而堆用來存儲靜態變數和常量。實例都存在棧內。
2.靜態方法和屬性載入條件(我就知道3種)
a 直接調用方法或屬性。
b 載入類
c 執行 靜態塊
至於如何分配不清楚。
3 有靜態內部類很常用。
4.我們都知道子類初始化前要先初始化父類。父類的屬性當然要分配空間,我總感覺這個是在棧內。總之感覺堆內放一些靜態的東西,而棧是放一些生命周期短些的東西
E. 堆與棧在內存里是怎麼分配的
堆和棧的區別(內存和數據結構)
在計算機領域,堆棧是一個不容忽視的概念,我們編寫的C語言程序基本上都要用到。但對於很多的初學著來說,堆棧是一個很模糊的概念。堆棧:一種數據結構、一個在程序運行時用於存放的地方,這可能是很多初學者的認識,因為我曾經就是這么想的和匯編語言中的堆棧一詞混為一談。我身邊的一些編程的朋友以及在網上看帖遇到的朋友中有好多也說不清堆棧,所以我想有必要給大家分享一下我對堆棧的看法,有說的不對的地方請朋友們不吝賜教,這對於大家學習會有很大幫助。
數據結構的棧和堆
首先在數據結構上要知道堆棧,盡管我們這么稱呼它,但實際上堆棧是兩種數據結構:堆和棧。
堆和棧都是一種數據項按序排列的數據結構。
棧就像裝數據的桶或箱子
我們先從大家比較熟悉的棧說起吧,它是一種具有後進先出性質的數據結構,也就是說後存放的先取,先存放的後取。這就如同我們要取出放在箱子裡面底下的東西(放入的比較早的物體),我們首先要移開壓在它上面的物體(放入的比較晚的物體)。
堆像一棵倒過來的樹
而堆就不同了,堆是一種經過排序的樹形數據結構,每個結點都有一個值。通常我們所說的堆的數據結構,是指二叉堆。堆的特點是根結點的值最小(或最大),且根結點的兩個子樹也是一個堆。由於堆的這個特性,常用來實現優先隊列,堆的存取是隨意,這就如同我們在圖書館的書架上取書,雖然書的擺放是有順序的,但是我們想取任意一本時不必像棧一樣,先取出前面所有的書,書架這種機制不同於箱子,我們可以直接取出我們想要的書。
內存分配中的棧和堆
然而我要說的重點並不在這,我要說的堆和棧並不是數據結構的堆和棧,之所以要說數據結構的堆和棧是為了和後面我要說的堆區和棧區區別開來,請大家一定要注意。
下面就說說C語言程序內存分配中的堆和棧,這里有必要把內存分配也提一下,大家不要嫌我啰嗦,一般情況下程序存放在Rom或Flash中,運行時需要拷到內存中執行,內存會分別存儲不同的信息,如下圖所示:
內存中的棧區處於相對較高的地址以地址的增長方向為上的話,棧地址是向下增長的。
棧中分配局部變數空間,堆區是向上增長的用於分配程序員申請的內存空間。另外還有靜態區是分配靜態變數,全局變數空間的;只讀區是分配常量和程序代碼空間的;以及其他一些分區。
F. 內存的分配方式有哪幾種
內存的三種分配方式:
1. 從靜態存儲區分配:此時的內存在程序編譯的時候已經分配好,並且在程序的整個運行期間都存在。全局變數,static變數等在此存儲。
2. 在棧區分配:相關代碼執行時創建,執行結束時被自動釋放。局部變數在此存儲。棧內存分配運算內置於處理器的指令集中,效率高,但容量有限。
3. 在堆區分配:動態分配內存。用new/malloc時開辟,delete/free時釋放。生存期由用戶指定,靈活。但有內存泄露等問題。
常見內存錯誤及對策
1. 內存分配未成功,卻被使用。
對策:使用內存之前檢查是否分配成功。用p!=NULL判斷。
2. 內存分配成功,未初始化就被使用。
內存的預設值沒有統一的標准。大部分編譯器以0作為初始值,但不完全是。
對策:內存初始化時賦初值。
3. 內存操作越界。
對策:只能是小心了。
4. 釋放了內存,仍然使用。
(1) 使用顯示delete和free的野指針。
對策:釋放完內存,將指針置為NULL。
(2) 使用隱式delete和free的野指針。主要是指函數返回指向棧內存的指針或引用。
對策:當然是不要返回就可以了。
5. 未釋放內存,導致內存泄露。
用new/malloc開辟了內存,沒用delete/free釋放.
對策:new和delete的個數一定相同;malloc和free的個數一定相同;new[]和[]delete一定對應。
G. 淺析棧區和堆區內存分配的區別
1、棧區(stack)由編譯器自動分配釋放,存放函數的參數值,局部變數的值等,內存的分配是連續的,類似於平時我們所說的棧,如果還不清楚,那麼就把它想成數組,它的內存分配是連續分配的,即,所分配的內存是在一塊連續的內存區域內.當我們聲明變數時,那麼編譯器會自動接著當前棧區的結尾來分配內存.2、堆區(heap)一般由程序員分配釋放,若程序員不釋放,程序結束時可能由操作系統回收.類似於鏈表,在內存中的分布不是連續的,它們是不同區域的內存塊通過指針鏈接起來的.一旦某一節點從鏈中斷開,我們要人為的把所斷開的節點從內存中釋放.3、全局區(靜態區)(static)全局變數和靜態變數的存儲是放在一塊的,初始化的全局變數和靜態變數在一塊區域,未初始化的全局變數和未初始化的靜態變數在相鄰的另一塊區域。程序結束後由系統釋放4、文字常量區常量字元串就是放在這里的。程序結束後由系統釋放5、程序代碼區存放函數體的二進制代碼。
H. Android 內存溢出和內存泄漏的區別
區別:
內存溢出就是要求分配的內存超出了系統能給的,系統不能滿足需求,於是產生溢出。
內存泄漏是指向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果申請到的那塊內存自己也不能再訪問(也許把它的地址給弄丟了),而系統也不能再次將它分配給需要的程序。
一個盤子用盡各種方法只能裝4個果子,你裝了5個,結果掉倒地上不能吃了。這就是溢出!比方說棧,棧滿時再做進棧必定產生空間溢出,叫上溢,棧空時再做退棧也產生空間溢出,稱為下溢。就是分配的內存不足以放下數據項序列,稱為內存溢出.
定義:
1.內存溢出 out of memory
是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
2.內存泄露 memory leak
是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。
3.二者的聯系
內存泄露最終會導致內存溢出
I. 誰給我解釋下,什麼是在棧上分配內存,與在堆上分配內存
內存分配方式有三種:
(1)從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變數,static變數。
(2)在棧上創建。在執行函數時,函數內局部變數的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
(3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。
J. 進程棧空間內存分配時機
可以。
堆:一般由程序員分配釋放,它的分配方式類似於鏈表。(2)申請後系統的響應:棧:只要所申請的空間小於棧的剩餘空間,則系統為程序分配內存,否則棧溢出。堆:操作系統有一個記。