理解Java類載入器之靜態變數總結
Android是用Java開發,其靜態變數的生命週期遵守Java的設計。靜態變數是在類被load的時候分配記憶體的,並且存在於方法區。當類被解除安裝的時候,靜態變數被銷燬。在PC機的客戶端程式中,一個類被載入和解除安裝,可簡單的等同於jvm程序的啟動和結束。那麼在Android中呢?用的Dalvik vm也是一樣的。不過Android不太突出的程序概念,所以對靜態變數的生命週期就會感覺模糊,這種模糊對於值型別是無所謂的,如果是靜態的物件引用,則與記憶體回收、記憶體洩漏這些問題有關,有必要加深研究和理解。
一、靜態變數在類被載入的時候分配記憶體。
類在什麼時候被載入?
當我們啟動一個app的時候,系統會建立一個程序,此程序會載入一個Dalvik VM的例項,然後程式碼就執行在DVM之上,類的載入和解除安裝,垃圾回收等事情都由DVM負責。也就是說在程序啟動的時候,類被載入,靜態變數被分配記憶體。
二、靜態變數在類被解除安裝的時候銷燬。
類在什麼時候被解除安裝?
在程序結束的時候。
說明:一般情況下,所有的類都是預設的ClassLoader載入的,只要ClassLoader存在,類就不會被解除安裝,而預設的ClassLoader生命週期是與程序一致的,本文討論一般情況。
三、Android中的程序什麼時候結束
這個是Android對程序和記憶體管理不同於PC的核心——如果資源足夠,Android不會殺掉任何程序,另一個意思就是程序隨時可能會被殺掉。而Android會在資源夠的時候,重啟被殺掉的程序。也就是說靜態變數的值,如果不做處理,是不可靠的,可以說記憶體中的一切都不可靠。如果要可靠,還是得儲存到Nand或SD卡中去,在重啟的時候恢復回來。
另一種情況就是不能把退出所有Activity等同於程序的退出,所以在使用者點選圖示啟動應用的時候,以前存放於靜態變數中的值,有可能還存在,因此要視具體情況給予清空操作。
四、Application也是一樣不可靠
Application其實是一個單例物件,也是放在記憶體中的,當程序被殺掉,就全清空了,只不過Android系統會幫重建Application,而我們存放在Application的資料自然就沒有了,還是得自己處理。
五、靜態引用的物件不會被垃圾回收
只要靜態變數沒有被銷燬也沒有置null,其物件一直被保持引用,也即引用計數不可能是0,因此不會被垃圾回收。因此,單例物件在執行時不會被回收。
static修飾的靜態變數,在不同的類和包中都可以使用,在虛擬機器中單獨佔用記憶體,但是可能會出現NullException的異常。
static訪問是無法跨程序的。Android中的Activity,Service是可以在各自程序中執行的,用static傳遞引數到不同程序的Activity、Service時會錯。
static佔據的那份記憶體,在接個電話或者長時間待機後,再回到應用也會出現崩潰的現象,而這些崩潰都與靜態變數的空指標有關係。
根據Google官方的推薦,我們應該儘量使用繼承自Application的自定義類,在我們繼承的類中定義需要全域性使用的變數,並通過getApplicationContext()來獲取和儲存相關的變數即可。
啟動Application時,系統會建立一個PID,即程序ID,所有的Activity就會在此程序上執行。那麼我們在Application建立的時候初始化全域性變數,同一個應用的所有Activity都可以取到這些全域性變數的值,換句話說,我們在某一個Activity中改變了這些全域性變數的值,那麼在同一個應用的其他Activity中值就會改變。
如果想在整個應用中使用全域性變數,在java中一般是使用靜態變數,public型別;而在android中如果使用這樣的全域性變數就不符合Android的框架架構,但是可以使用一種更優雅的方式就是使用Application context。
前段時間公司測試人員告訴我,在測試機上有段時間沒執行的app再去測試的時候突然崩潰了。
事實上再次啟動程式之後,這個問題就不存在了,一切都是那麼正常,但是這種莫名其妙的bug怎麼可以放任它存在了,最後也是一頓折騰總算是找到了原因,看到標題應該有人明白怎麼回事了,就是static,現在就在這裡好好說說。
static 修飾的靜態變數,在不同的類和包中都可以使用,系統為我們app建立虛擬機器之後,類被載入,靜態變數被分配記憶體,並且在虛擬機器中單獨佔用記憶體,靜態變數在類被解除安裝的時候才會被銷燬,而類只有在程序結束的時候才會被解除安裝,也就是說被static修飾的靜態變數只有在程序被銷燬的時候才會被回收 。在我之前的概念裡,一個app程序只有被主動退出或者主動清理後才會被銷燬,但是事實上,android 系統在資源不足的情況下就會kill掉程序已保證系統正常執行,通常情況下會優先kill掉程序優先順序比較低的程序,例如處於後臺長時間沒有執行的程序,但系統在記憶體極少的情況下,依然會選擇kill 掉一些前臺程序,系統kill程序順序:後臺程序 -> 服務程序 -> 可見程序 -> 前臺程序。說道這裡,也就可以解釋我程式為什麼會崩潰的原因了:在我進入某一特定介面activityA之前,會給某一static靜態變數B賦值,這樣在activityA我就可以使用B來進行一些邏輯操作,但是在某一未知時間,系統由於資源緊張而kill掉我的程序,然後在資源充足的情況重新恢復了程序恢復了activityA,造成了當前程序未被kill掉的假象,由於程序被重新建立,原來被賦值的靜態變數B被重置,那麼在我操作B的時候由於沒有做空值檢測,丟擲空指標,so , game over…
詳情可以參考:
找到了原因那麼就說說解決辦法:
1. 儘量少使用static來修飾變數,尤其是一些關鍵性的資料,並且對於被修飾過的變數在使用前要進行空值檢測;
2. 對於一些重要資訊,可以選擇本地持久化
3. 善用onSaveInstanceState及onRestoreInstanceState方法對資料進行儲存和恢復,當然在oncreat方法裡我們也可以使用savedInstanceState對資料進行恢復,
關於onSaveInstanceState及onRestoreInstanceState被呼叫的時機:
onRestoreInstanceState方法在onStart之後被呼叫,此時可以進行資料恢復(前提是有進行了資料儲存操作);
onSaveInstanceState方法在onPause之後被呼叫;
注意:系統只在Activity異常終止的時候才會呼叫onSaveInstanceState與onRestoreInstanceState來儲存和恢復資料,其他情況不會觸發這個過程。但是按Home鍵或者啟動新Activity仍然會單獨觸發onSaveInstanceState的呼叫
相關推薦
理解Java類載入器之靜態變數總結
Android是用Java開發,其靜態變數的生命週期遵守Java的設計。靜態變數是在類被load的時候分配記憶體的,並且存在於方法區。當類被解除安裝的時候,靜態變數被銷燬。在PC機的客戶端程式中,一個類被載入和解除安裝,可簡單的等同於jvm程序的啟動和結束。那麼在Androi
Java程式設計師從笨鳥到菜鳥之(九十八)深入java虛擬機器(七)深入原始碼看java類載入器ClassLoader
歡迎閱讀本專題的其他部落格: ClassLoader類載入器是負責載入類的物件。ClassLoader 類是一個抽象類。如果給定類的二進位制名稱(即為包名加類名的全稱),那麼類載入器會試圖查詢或生成構成類定義的資料。一般策略是
java類載入器——ClassLoader
web rac rgb 好的 全盤負責機制 安全 trac 字節 如何 Java的設計初衷是主要面向嵌入式領域,對於自己定義的一些類,考慮使用依需求載入原則。即在程序使用到時才載入類,節省內存消耗,這時就可以通過類載入器來動態載入。 假設你平時僅僅是做web開發,那應該
Java類載入器 ClassLoader的解析
index html dir obj ble body 6.4 odin 普通 //參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 類載入器基本概念 類載
JAVA類載入器詳解
Java類載入器的作用就是在執行時載入類。Java類載入器基於三個機制:委託、可見性和單一性。委託機制是指將載入一個類的請求交給父類載入器,如果這個父類載入器不能夠找到或者載入這個類,那麼再載入它。可見性的原理是子類的載入器可以看見所有的父類載入器載入的類,而父類載入器看不到子類載入器載入的
1.java類載入器?
Java類載入器ClassLoader總結 JAVA類裝載方式,有兩種: 1.隱式裝載, 程式在執行過程中當碰到通過new 等方式生成物件時,隱式呼叫類裝載器載入對應的類到jvm中。 2.顯式裝載, 通過class.forname()等方法,顯式載入需要的類 類載
Java類載入器(死磕5)
Java類載入器( CLassLoader ) 死磕5: 自定義一個檔案系統classLoader 本小節目錄 5.1. 自定義類載入器的基本流程 5.2. 入門案例:自定義檔案系統類載入器 5.3. 案例的環境配置 5.4 FileClassLoader
Java類載入器( 死磕9)
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文
Java類載入器( 死磕7)
【正文】Java類載入器( CLassLoader )死磕7: 基於加密的自定義網路載入器 本小節目錄 7.1. 加密傳輸Server端的原始碼 7.2. 加密傳輸Client端的原始碼 7.3. 使用亦或實現簡單加密和解密演算法 7. 網路加密SafeClassLoader的原始
Java類載入器( 死磕 4)
【正文】Java類載入器( CLassLoader ) 死磕 之4: 神祕的雙親委託機制 本小節目錄 4.1. 每個類載入器都有一個parent父載入器 4.2. 類載入器之間的層次關係 4.3. 類的載入次序 4.4 雙親委託機制原理與沙箱機制 4.5. forName
Java類載入器(死磕3)
【正文】Java類載入器( CLassLoader ) 死磕3: 揭祕 ClassLoader抽象基類 本小節目錄 3.1. 類的載入分類:隱式載入和顯示載入 3.2. 載入一個類的五步工作 3.3. 如何獲取類的載入器 3.4 解刨載入器——ClassLoade
Java類載入器(死磕 1-2)
Java類載入器( CLassLoader ) 死磕 1、2: 匯入 & 類載入器分類 本小節目錄 1.匯入 1.1. 從class檔案的載入開始 1.2. 什麼是類載入器 2. JAVA類載入器分類 2.1. 作業系統的環境變數 2.2. Bo
Java類載入器( 深磕8)
【正文】Java類載入器( CLassLoader ) 深磕 8: 使用ASM,和類載入器實現AOP 本小節目錄 8.1. ASM位元組碼操作框架簡介 8.2. ASM和訪問者模式 8.3. 用於增強位元組碼的事務類 8.4 通過ASM訪問註解 8.5. 通過ASM注入
Java類載入器( 深磕9)
【正文】Java類載入器( CLassLoader ) 深磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5.
Java類載入器( 深磕7)
【正文】Java類載入器( CLassLoader )深磕7: 基於加密的自定義網路載入器 本小節目錄 7.1. 加密傳輸Server端的原始碼 7.2. 加密傳輸Client端的原始碼 7.3. 使用亦或實現簡單加密和解密演算法 7. 網路加密SafeClassLoa
Java類載入器( 深磕 6)
【正文】Java類載入器( CLassLoader )深磕 6: 自定義網路類載入器 本小節目錄 6.1. 自定義網路類載入器的類設計 6.2. 檔案傳輸Server端的原始碼 6.3. 檔案傳輸Client端的原始碼 6. 4 自定義載入器SocketClassLoade
Java類載入器( 深磕 4)
【正文】Java類載入器( CLassLoader ) 之4: 神祕的雙親委託機制 本小節目錄 4.1. 每個類載入器都有一個parent父載入器 4.2. 類載入器之間的層次關係 4.3. 類的載入次序 4.4 雙親委託機制原理與沙箱機制 4.5. forName方法和
Java類載入器(深磕3)
【正文】Java類載入器( CLassLoader ) 深磕3: 揭祕 ClassLoader抽象基類 本小節目錄 3.1. 類的載入分類:隱式載入和顯示載入 3.2. 載入一個類的五步工作 3.3. 如何獲取類的載入器 3.4 解刨載入器——ClassLoade
java類載入器以及spi
類載入器概述: 每個編寫的”.java”拓展名類檔案都儲存著需要執行的程式邏輯,這些”.java”檔案經過Java編譯器編譯成拓展名為”.class”的檔案,”.class”檔案中儲存著Java程式碼經轉換後的虛擬機器指令,當需要使用某個類時,虛擬機器將會載入它的”.class”檔案,並
Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文