《Android虛擬機》--內存分配策略


No1:

Java在內存分配時會涉及到以下區域:

寄存器:我們在程序中無法控制

:存放基本類型的數據和對象的引用,但對象本身不存放在棧中,而是存放在堆中

:存放用new產生的數據

靜態域:存放在對象中用static定義的靜態成員

常量池:存放常量

非RAM存儲:硬盤等永久存儲空間

No2:

棧中的數據都是以棧幀(Stack Frame)的格式存在的。棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集。

棧幀中主要保存如下3種數據:

1)本地變量(Local Variables):包括輸入參數和輸出參數以及方法內的變量

2)棧操作(Operand Stack):記錄出棧、入棧的操作

3)棧幀數據(Frame Data):包括類文件、方法等

No3:

堆內存用來存放由關鍵字new創建的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理

No4:

引用變量是普通的變量,定義時再棧中分配,引用變量在程序運行到其作用域之外后被釋放。而數組和對象本身在堆中分配,即使程序運行到使用new產生數組或者對象的語句所在的代碼塊之外,數組和對象本身占據的內存不會被釋放,數組和對象在沒有引用變量指向它時,才變為垃圾,不能再被使用,但仍然占據內存空間不放,在隨后的一個不確定的時間被垃圾回收器收走(釋放掉)。這也是Java比較占內存的原因。實際上,棧中的變量指向堆內存中的變量,這就是Java中的指針。

No5:

常量池指的是在編譯期被確定,並被保存在已編譯的.class文件中的一些數據。除了包含代碼中所定義的各種基本類型和對象型(如string及數組)的常量值(final)還包含一些以文本形式出現的符號引用。

1)類和接口的全限定名 2)字段的名稱和描述符 3)方法的名稱和描述符

虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集合,包括直接常量(string,integer和floating point常量)和對其他類型,字段和方法的符號引用。

No6:

一個Java虛擬機實例只存在一個堆內存,堆內存的大小是可以調節的。類加載器讀取了類文件后,需要把類、方法、常變量放到堆內存中,以方便執行器執行,堆內存分為三部分:

1)Permanent Space 永久存儲區

永久存儲區是一個常駐內存區域,它存儲的是運行環境必需的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉Java虛擬機才會釋放此區域所占用的內存。

2)Young Generation Space 新生區

3)Tenure generation space 養老區

No7:

是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。

No8:

的優勢是存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。

No9:

Java虛擬機內存模型中定義的訪問操作與物理計算機處理的基本一致。Java通過多線程機制使得多個任務同時執行處理,所有的線程共享Java虛擬機內存區域main memory,而每個線程又單獨的有自己的工作內存,當線程與內存區域進行交互時,數據從主存復制到工作內存,進而交由線程處理(操作碼+操作數)。

No10:

運行時的數據區域:程序計數器(Program Counter Register)、Java的虛擬機棧(VM Stack)、本地方法棧(Natvie Method Stack)、Java堆(Java Heap)、方法區、運行時常量池、直接內存

No11:

程序計數器

在Java虛擬機的概念模型里,字節碼解釋器通過改變這個計數器的值來選取下一條需要執行的字節碼指令。為了在線程切換后能恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,並且各條線程之間的計數器互不影響,能夠獨立存儲,我們稱這類內存區域為線程私有的內存。

此內存區域是唯一一個在java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域。

No12:

Java的虛擬機棧

虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程

下面列出的兩種異常情況與Java棧相關

1)如果線程請求的棧深度大於虛擬機所允許的深度,則Java虛擬機將拋出StackOverflowError異常

2)如果虛擬機棧可以動態擴展,但是無法申請到足夠的內存來實現擴展,或者不能得到足夠的內存為一個新線程創建初始Java棧,則Java虛擬機將拋出OutOfMemorError異常

No13:

本地方法棧:

本地方法棧中執行的是非Java語言編寫的代碼,例如C或C++,它們通常在每個線程被創建時分配在每個線程被創建時分配在每個線程基礎上的。

No14:

Java堆:

Java堆是類實例和數組的分配空間,是一塊所有線程共享的內存區域。堆在虛擬機啟動時創建,是Java虛擬機所管理的內存中最大的一塊。Java堆內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。

No15:

方法區:

方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器變異后的代碼等數據。

No16:

運行時常量池:

用於存放編譯器生成的各種字面量和符號引用,常量池也是方法區的一部分。

No17:

訪問對象的主流方式有兩種:

1)使用句柄:Java堆中將會划分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據和類型數據各自的具體地址信息。

2)使用直接指針:Java堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,reference中直接存儲的就是對象地址。

兩種訪問方式各有優勢

使用句柄訪問方式的最大好處就是reference中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference本身不需要被修改

使用直接指針訪問方式最大好處就是速度更快,它節省了一次指針定位的時間開銷,由於對象的訪問在Java中非常頻繁,因此這類開銷積少成多猴也是一項非常可觀的執行成本

No18:

內存泄露的分類:

1)常發性內存泄露

發生內存泄露的代碼會被多次執行,每次被執行的時候都會導致一塊內存泄露

2)偶發性內存泄露

發生內存泄露的代碼只有在某些特定環境下或操作過程中才會發生。

3)一次性內存泄露

發生內存泄露的代碼只會被執行一次,或者由於算法上的缺陷,導致總會有一塊且僅一塊內存發生泄露。

4)隱式內存泄露

程序在運行過程中不停地分配內存,但是直到程序結束時才釋放內存。對於一個服務器程序來說,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。

No19:

內存管理的核心就是兩個部分:分配內存和回收內存。Java語言使用new操作符來分配內存

No20:

所有的對象都有一個相同的頭部clazz和lock:

1)clazz:指向該對象的類對象,類對象用來描述該對象所屬的類,這樣可以很容易的從一個對象獲取該對象所屬的類的具體信息

2)lock:是一個無符號整數,用以實現對象的同步

3)data:用於存放對象數據,根據對象的不同數據區的大小是不同的

No21:

在Dalvik虛擬機實現有3個時機可以觸發垃圾收集的運行

1)程序員顯式的調用System.gc()

2)內存分配失敗時

3)如果分配的對象大小超過384KB,運行並發標記(concurrent mark)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM