1. 程式人生 > >從JVM角度來看物件

從JVM角度來看物件

在Java中要建立一個物件最簡單方法就是new,當然大部分情況下我們還是通過spring來管理物件。但對於JVM來說一個物件的建立、存亡可沒那麼簡單了。

物件的建立

虛擬機器遇到一條new指令時,首先去檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已經被載入、解析和初始化。如果沒有,則必須先執行相應的類載入過程。參考:關於類載入機制
在類載入檢查通過後,接下來虛擬機器將為新生物件分配記憶體。物件所需記憶體的大小在類載入完成後就可以完全確定,給物件分配空間的任務就等同於把一塊確定大小的記憶體從Java堆中劃分出來。

假設Java堆中記憶體時絕對規整的,所有用過的記憶體都放一邊,空閒的記憶體放另一邊,中間放著一個指標作為分界點的指示器,那所分配記憶體就僅僅是吧指標像空閒的那邊移動一段與物件大小相等的距離,這種分配方式稱為“指標碰撞

”。

假設Java堆中記憶體並不規整,已使用的記憶體和空閒相互交錯,指標碰撞肯定不行,虛擬機器就必須維護一個列表,記錄哪些記憶體塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給物件例項,並更新列表上的記錄,這種分配方式稱為“空間列表”。選擇哪種分配方式有Java堆是否規整決定,而Java堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。因此,在使用Serial、ParNew等帶Compact過程的收集器時,系統採用的分配演算法是指標碰撞,而使用CMS這種基於標記-清除演算法的收集器時,通常採用空閒列表。

當然不管哪種分配方式,在併發情況下是執行緒不安全的,即使是僅僅修改一個指標所指向的位置,比如物件A分配記憶體,指標還沒來的及修改,物件B又同時使用了原來的指標來分配記憶體情況。有兩種解決方案,一種是對分配記憶體空間的動作進行同步處理——實際上虛擬機器採用CAS配上失敗重試的方式保證更新新操作的原子性(這種方法被經常使用比如ConcurrentHashMap);另一種是把記憶體分配的動作按照執行緒劃分在不同的的空間中進行,即每個執行緒在Java堆中預先分配一小塊記憶體,稱為本地現場分配緩衝(Thread Local Allocation Buffer,TLAB)。哪個執行緒要分配記憶體,就在哪個執行緒的TABLE上分配,只有TABLE用完並分配新的TLAB時,才需要同步鎖定。虛擬機器是否使用TLAB,可以通過-XX:+/-UseTLAB引數來設定。
記憶體分配完後,虛擬機器需要將分配到的記憶體空間都初始化為零值(不包括物件頭),如果使用TLAB,這一工作過程也可以提前至TLAB分配時進行。這一步操作保證了物件的例項欄位在Java程式碼中可以不賦初始值就直接使用,程式能訪問到這些欄位的資料型別所對應的零值。接下來,虛擬機器要對物件進行必要的設定,例如這個物件是哪個類的例項、如何才能找到類的元資料資訊、物件的雜湊碼、物件的GC分代年齡等資訊。這些資訊存放在物件的物件頭中。至此從虛擬機器的角度來看,一個新的物件已經產生了,但從Java程式的角度來看,物件建立才剛剛開始——方法還沒執行,所有的欄位都還為零。

物件的訪問

我們知道Java程式需要通過站上的reference資料來操作堆上的具體物件(可以參考:Java記憶體區域詳解)。Java虛擬機器規範中只規定了一個指向物件的引用,並沒有定義這個引用應該通過何種方式去定位訪問,所有物件的訪問方式是由虛擬機器來決定的,目前主流的訪問方式有使用控制代碼直接指標兩種。
控制代碼訪問:如果使用控制代碼訪問的話,Java堆中將會劃分出一塊記憶體來作為控制代碼池,reference中儲存的就是物件的控制代碼地址,而控制代碼中包含了物件例項資料與型別資料各自的具體地址資訊。如圖:
這裡寫圖片描述
直接指標:如果使用直接指標訪問,那麼Java堆物件的佈局中就必須考慮如何放置訪問型別資料的相關資訊,而reference中儲存的直接就是物件地址如圖:
這裡寫圖片描述

兩種訪問方式各有優勢,使用控制代碼訪問最大的好處就是reference中儲存的是穩定的控制代碼地址,在物件被移動時只會改變控制代碼中的例項資料指標,而reference本身不需要修改。
而是用直接指標訪問方式的最大好處就是速度更快,它節省了一次指標定位的時間開銷,由於物件的訪問在Java中非常頻繁,因此這類開銷積少成多也是非常可觀的執行成本。

物件的存亡

在Java中幾乎所有的例項物件都存在堆空間,垃圾收集器在對堆進行回收前,第一件事情就是要確定這些物件哪些還存活哪些已經死去(即不可能再被任何途徑使用的物件),而判斷物件的存亡有兩種:引用計數演算法和可達性分析演算法

引用計數演算法
給物件中新增一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;當計數值為0時說明此物件已經不再被使用。引用計數演算法的實現簡單,判定效率高,有一些大型公司使用此方式如微軟。但主流的Java虛擬機器沒有選用此種方式來管理記憶體,其中主要的原因是它很難解決物件之間的相互迴圈引用問題。

public class ReferenceCountingGC {  
    public Object instance = null;  

    private static final int _1MB = 1024 * 1024;  

    /** 
     * 
     */  
    private byte[] bigSize = new byte[2 * _1MB];  

    public static void main(String[] args) {  
        ReferenceCountingGC objA = new ReferenceCountingGC();  
        ReferenceCountingGC objB = new ReferenceCountingGC();  
        objA.instance = objB;  
        objB.instance = objA;  

        objA = null;  
        objB = null;  

        //假設在這行發生了GC,objA和ojbB是否被回收  
        System.gc();  
    }  
}  

可達性分析演算法
在主流的商用程式語言的主流實現中,都是通過可達性分析演算法來判定物件的存活。這個演算法的基本思路是童工一系列稱為“GC Roots”的物件作為起始點,從這些節點開始 向下搜尋,搜尋所走過的路徑稱為引用鏈,當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件不可用。如圖:雖然物件8、9、10、11、12相互有引用但是它們到GC Roots是不可達的,所以被判定為可回收物件。
這裡寫圖片描述

在Java語言中,可作為GC Roots的物件包括下面幾種:
虛擬機器棧(棧幀中的本地變量表)中引用的物件
方法區中類靜態屬性引用的物件
方法區中常量引用的物件
本地方方棧中Native方法引用的物件

本文來自《深入理解Java虛擬機器》

相關推薦

JVM角度來看物件

在Java中要建立一個物件最簡單方法就是new,當然大部分情況下我們還是通過spring來管理物件。但對於JVM來說一個物件的建立、存亡可沒那麼簡單了。 物件的建立 虛擬機器遇到一條new指令時,首先去檢查這個指令的引數是否能在常量池中定位到一個類的符號

趙晨雨: 微觀角度來看linux核心設計

◆◆從微觀角度來看Linux核心設計◆◆ 餘生皆歡喜 最近總結出來學習核心有兩個大的角度,一種就是從巨集觀角度來看,總的來說就是順著抽象,管理,操作來看,這種角度更多的是核心中應用層面的內容,用來理解核心中是怎麼運轉起來的。第二種就是從核心的最細節部分出發,深入到一個個具體的巨

JVM角度理解執行緒

http://blog.csdn.net/iter_zc/article/details/41843595 程式設計技術交流請加QQ群:點選連結加入群【Just Do IT】:https://jq.qq.com/?_wv=1027&k=478lBF3

jvm角度看java多執行緒

最近在學習jvm,發現隨著對虛擬機器底層的瞭解,對java的多執行緒也有了全新的認識,原來一個小小的synchronized關鍵字裡別有洞天。決定把自己關於java多執行緒的所學整理成一篇文章,從最基礎的為什麼使用多執行緒,一直深入講解到jvm底層的鎖實現。 多執行緒的目的 為什麼要使用多執

IDEA角度來看懂UML圖

前言 我們目前已經學習了設計模式的7種設計原則。下面本該是直接進入具體的設計模式系列文章。 但是呢在我們學習設計模式之前我們還是有必要了解一下uml圖。因為後續的設計模式文章不出意外應該會很多地方使用到uml圖。如果你連uml圖都看不懂的話,那麼學習起來肯定會有一定的難度。 所以說,這一節就作為承上啟下的章節

jvm角度來看java的多執行緒

最近在學習jvm,發現隨著對虛擬機器底層的瞭解,對java的多執行緒也有了全新的認識,原來一個小小的synchronized關鍵字裡別有洞天。決定把自己關於java多執行緒的所學整理成一篇文章,從最基礎的為什麼使用多執行緒,一直深入講解到jvm底層的鎖實現。 多執行緒的目的

Java三大特性之多型:JVM角度來看多型

Java三大特性:封裝、繼承、多型,相信大家都有所瞭解,今天我們主要從JVM的角度來學習一下多型。 進入正題之前,先帶著大家來快速的溫故一下什麼是封裝、繼承、多型。 1,封裝:把專案中用的比較多的、可複用的功能封裝成一個Java類,或者封裝成一個方法,然後只要用到該功能,直

Python的角度來看編碼與解碼

異常 字符 default 疑問 習慣 中文字符集 nbsp prompt ans 導語: Python2和Python3中,因為默認字符集的不同而造成的麻煩,簡直是程序員的夢魘!要徹底告別這個麻煩,就需要從本質上來理解編碼和解碼。 為什麽要有編碼? 對於不會英文的中國

系統架構的角度來看開源和商業負載均衡

對於絕大多數的網際網路企業來說,負載均衡已經成為其系統網路架構中不可或缺的組成部分。負載均衡技術是終端使用者與業務系統之間溝通的橋樑,也是實現系統處理能力快速擴充套件的最佳技術。實現負載均衡的方式很多,有很多開源的負載均衡軟體,如:LVS、HAProxy、nginx等,也有很多公司推出獨立的負載均衡

JVM角度看Spring的AOP

    以下觀點,是個人對AOP底層實現的理解。由於個人知識的侷限性,難免有錯誤,僅 供參考。     我們以Spring的事務管理機制為切入點,來進行說明。並且,以下所有觀點,都是建 立在2個前提條件下: 1,Spring是在程式執行期間,把事務控制程式碼新增到委託類的位

“思考”的角度來看如何成為一名優質的Java架構師

導讀: 架構師應不應該寫程式碼 為什麼別人的系統總是那麼爛 成為架構師最困難的門檻是什麼? 如何更高效的學習? 1.架構師應不應該寫程式碼 合格的程式設計師對於明確分配的任務會完成的很好,但是大部分情況下“架構”這個詞意味著架構師並不會涉及太多細節,架構圖和程

jvm來看,scala中的@究竟是個什麼鬼?@模式匹配符號(scala 詞法分析 語法分析 常用)

從jvm來看,scala中的@究竟是個什麼鬼? 我也是初步嘗試來看jvm的類檔案,又是初次來分析@,如不對的地方,請各位指正! 先看一下@ 是個什麼? /** * Created by admin on 2016/12/29. */ object test7 {

程式設計師的角度來看為什麼我們需要工作流

     每一個程式設計師,在接觸到工作流的時候,都會有這麼一個疑問——我用一般的方法可以實現,為什麼還要用工作流?      我曾經也問過這個問題,不過現在稍微有點明白了。彆著急要答案,看過下面

JVM記憶體管理的角度談談JAVA類的靜態方法和靜態屬性

JVM的記憶體分為兩部分: stack(棧)是JVM的記憶體指令區。stack管理很簡單,push一定長度位元組的資料或者指令,stack指標壓棧相應的位元組位移; pop一定位元組長度資料或者指令,stack指標彈棧。stack的速度很快,管理很簡單,並且每次操作的資料或

原始碼的角度來看SpringMVC

SpringMVC核心流程圖 簡單總結 首先請求進入DispatcherServlet 由DispatcherServl

「每日五分鐘,玩轉JVM」:物件哪來

面向物件 眾所周知,Java是一門面向物件的高階程式語言,那麼現在問題來了,物件從哪來呢?有些人會說通過new關鍵字來建立一個物件,說的很好,本篇我們就來解密在new一個物件的過程中,JVM都給我們做了什麼工作。 走哪來,到哪去 一個物件的誕生必定有一個類,通常我們都是通過new關鍵字例項化一個類來獲取該類的

JVM系列之:彙編角度分析Volatile

[toc] # 簡介 Volatile關鍵字對熟悉java多執行緒的朋友來說,應該很熟悉了。Volatile是JMM(Java Memory Model)的一個非常重要的關鍵詞。通過是用Volatile可以實現禁止重排序和變數值執行緒之間可見兩個主要特性。 今天我們從彙編的角度來分析一下Volatile

JVM系列之:彙編角度分析NullCheck

[toc] # 簡介 之前我們在講Virtual call的時候有提到,virtual call方法會根據傳遞的引數例項的不同而進行優化,從而優化成為classic call,從而提升執行效率。 今天我們考慮一下,在virtual call中執行nullcheck的時候,如果已經知道傳遞的引數是非空的。

規範的角度解析物件 — 原始值轉換

# 物件 — 原始值轉換 當物件相加 `obj1 + obj2`,相減 `obj1 - obj2`,或者使用 `alert(obj)` 列印時會發生什麼? 在這種情況下,物件會被自動轉換為原始值,然後執行操作。 在 [型別轉換](https://zh.javascript.info/object-top

架構師之路--業務角度談緩存的選型

inno 基於 時間限制 更新 負載 聚集 穿透 同步 寫入   想起來幾年前挺火的前島國國民女神學霸-小島方晴子。當時替她說話的人都很慘,導師被逼自殺。她收到的壓力侮辱不是常人可以想象的。但是她卻堅強的活著,去年還出了書。我去日本的時候,下了新幹線,前面有一群女學生,她們