相信大部分童靴都有聽說個名詞:
內存洩漏 究竟什麼是內存洩漏,它具體有什麼危害呢?且聽我娓娓道來
JVM/ART
首先為大家普及下,java運行時是有一套虛擬機機制存在的:
JVM(Java虛擬機)
是一個虛構出來的運行Java程序的運行時,是通過在實際的計算機上仿真模擬各種計算機功能的實現。它具有完善的硬件架構(如處理器、堆棧、寄存器等),還具有相應的指令系統,使用JVM就是使Java程序支持與操作系統無關。
理論上在任何操作系統中,只要有對應的JVM,即可運行Java程序。
ART(android虛擬機)
是在Android系統上運行Android程序的虛擬機,其指令集是基於寄存器架構的,執行特有的文件格式-dex字節碼來完成對象生命週期管理、堆棧管理、線程管理、安全異常管理、垃圾回收等重要功能。
其實ART就是在JVM基礎上專門為android移動設備定製的一套虛擬機方案。
內存洩漏/內存溢出
內存洩露(Memory leak)
程序在向系統申請分配內存空間後(new),在使用完畢後未釋放。結果導致一直佔據該內存單元,我們和程序都無法再使用該內存單元,直到程序結束,這是內存洩露。
內存溢出(out of memory)
程序向系統申請的內存空間超出了系統能給的,比如一車最多能坐5個人,你卻非要塞下10個,車就擠爆了。
大量的內存洩露會導致內存溢出(oom)
內存區域
JAVA是在JVM所虛擬出的內存環境中運行的,JVM的內存可分為三個區:
堆(heap)、棧(stack)和方法區(method)。
棧(stack)
是簡單的數據結構,但在計算機中使用廣泛。棧最顯著的特徵是:LIFO(Last In, First Out, 後進先出),棧中只存放基本類型和對象的引用(不是對象)
堆(heap)
堆內存用於存放由new創建的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。JVM只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身。
方法區(method)
又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變量
內存洩漏原因分析
那麼問題來了?究竟哪部分的內存會導致內存洩漏呢?
在JAVA中JVM的棧記錄了方法的調用,每個線程擁有一個棧。
在線程的運行過程當中,執行到一個新的方法調用,就在棧中增加一個內存單元,即幀(frame)。在frame中,保存有該方法調用的參數、局部變量和返回地址。
然而JAVA中的局部變量只能是基本類型變量(int),或者對象的引用。所以在棧中只存放基本類型變量和對象的引用。引用的對象保存在堆中。
當某方法運行結束時,該方法對應的frame將會從棧中刪除,frame中所有局部變量和參數所佔有的空間也隨之釋放。
線程回到原方法繼續執行,當所有的棧都清空的時候,程序也就隨之運行結束。
而對於堆內存,堆存放著普通變量。在JAVA中堆內存不會隨著方法的結束而清空,所以在方法中定義了局部變量,在方法結束後變量依然存活在堆中。
綜上所述,棧(stack)可以自行清除不用的內存空間。但是如果我們不停的創建新對象,堆(heap)的內存空間就會被消耗盡。
所以JAVA引入了垃圾回收(garbage collection,簡稱GC)去處理堆內存的回收,但如果對象一直被引用無法被回收,造成內存的浪費,無法再被使用。
所以對象無法被GC回收就是造成內存洩露的原因!
垃圾回收機制
垃圾回收(garbage collection,簡稱GC)可以自動清空堆中不再使用的對象。
在JAVA中對象是通過引用使用的。如果再沒有引用指向該對象,那麼該對象就無從處理或調用該對象,這樣的對象稱為不可到達(unreachable)。
垃圾回收用於釋放不可到達的對象所佔據的內存。
根據上圖可以知道:由
於obj4沒有root指向它,所以GC會釋放它obj7由於還有其他引用指向它,所以得不到釋放如果持有對象的強引用,垃圾回收器是無法在內存中回收這個對象。
所以內存洩露的真因是:
持有對象的強引用,且沒有及時釋放,進而造成內存單元一直被佔用,浪費空間,甚至可能造成內存溢出!
內存洩漏對應用的影響
內存洩漏對於app沒有直接危害,即使有發現內存洩漏的情況,也不一定會立即引起app崩潰,但是通過累積效應,應用會爆出各種問題:
內存得不到釋放,慢慢的會造成app內存溢出,導致崩潰
內存洩漏同時可能會觸發系統頻繁GC,發生內存抖動,會導致系統性能問題(卡頓不流暢)
閱讀更多 檸檬班軟件測試 的文章