1. 程式人生 > >理解Java類載入器之靜態變數總結

理解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. 執行緒上下文