第二章 JVM記憶體分配

来源:http://www.cnblogs.com/java-zhao/archive/2016/02/03/5180492.html
-Advertisement-
Play Games

註意:本篇博客,主要參考自以下三本書 《分散式Java應用:基礎與實踐》 《深入理解Java虛擬機(第二版)》 《突破程式員基本功的16課》 說明:關於JVM記憶體結構,查看《第一章 JVM記憶體結構》,下麵所講的JVM記憶體分配主要是指在Hotspot JVM下新建對象在堆記憶體中分配的情況。 1、創建一


註意:本篇博客,主要參考自以下三本書

《分散式Java應用:基礎與實踐》

《深入理解Java虛擬機(第二版)》

《突破程式員基本功的16課》

說明:關於JVM記憶體結構,查看《第一章 JVM記憶體結構》,下麵所講的JVM記憶體分配主要是指在Hotspot JVM下新建對象在堆記憶體中分配的情況。

 

1、創建一個真正對象的基本過程

五步:

  • 類載入機制檢查
    • JVM首先檢查一個new指令的參數是否能在常量池中定位到一個符號引用,並且檢查該符號引用代表的類是否已被載入、解析和初始化過(實際上就是在檢查new的對象所屬的類是否已經執行過類載入機制)。如果沒有,先進行類載入機制載入類。關於類載入機制,之後再說。
  • 分配記憶體
    • 把一塊兒確定大小的記憶體從Java堆中劃分出來
  • 初始化零值(操作實例數據部分--對象記憶體佈局三部分之一
    • 對象的實例欄位不需要賦初始值也可以直接使用其預設零值,就是這裡起得作用
    • 每一種類型的對象都有不同的預設零值
  • 設置對象頭(操作對象頭部分--對象記憶體佈局三部分之一
  • 執行<init>
    • 為對象的欄位賦值(在第三步只是初始化了零值,這裡會根據所寫程式給實例賦值)

 

2、記憶體分配概念

  • 在類載入完成後,一個對象所需的記憶體大小就可以完全確定了,具體的情況查看對象的記憶體佈局。
  • 為對象分配空間,即把一塊兒確定大小(上述確定下來的對象記憶體大小)的記憶體從Java堆中劃分出來

 

3、記憶體分配兩種方式

  • 指針碰撞
    • 適用場合:堆記憶體規整(即沒有記憶體碎片)的情況下
    • 原理:用過的記憶體全部整合到一邊,沒有用過的記憶體放在另一邊,中間有一個分界值指針,只需要向著沒用過的記憶體方向將該指針移動對象記憶體大小位置即可
    • GC收集器:Serial、ParNew
  • 空閑列表
    • 適用場合:堆記憶體不規整的情況下
    • 原理:虛擬機會維護一個列表,該列表中會記錄哪些記憶體塊是可用的,在分配的時候,找一塊兒足夠大的記憶體塊兒來劃分給對象實例(這一塊兒可以類比memcached的slab模型),最後更新列表記錄。關於memcached slab記憶體模型介紹查看《第六章 memcached剖析
    • GC收集器:CMS
  • 註意
    • 選擇以上兩種方式中的哪一種,取決於Java堆記憶體是否規整
    • Java堆記憶體是否規整,取決於GC收集器的演算法是"標記-清除",還是"標記-整理"(也稱作"標記-壓縮")

 

4、記憶體分配併發問題

堆記憶體是各個線程的共用區域,所以在操作堆記憶體的時候,需要處理併發問題。處理的方式有兩種:

  • CAS+失敗重試
  • TLAB(Thread Local Allocation Buffer)
    • 原理:為每一個線程預先在Eden區分配一塊兒記憶體,JVM在給線程中的對象分配記憶體時,首先在TLAB分配,當對象大於TLAB中的剩餘記憶體或TLAB的記憶體已用盡時,再採用上述的CAS進行記憶體分配
    • -XX:+/-UseTLAB:是否使用TLAB
    • -XX:TLABWasteTargetPercent設置TLAB可占用的Eden區的比率,預設為1%
    • JVM會根據以下三個條件來給每個線程分配合適大小的TLAB
      • -XX:TLABWasteTargetPercent
      • 線程數量
      • 線程是否頻繁分配對象
    • -XX:PrintTLAB:查看TLAB的使用情況

 

5、總結

  • 儘量少創建對象
    • 根據第一塊兒所說,創建一個對象的過程比較複雜,耗時較多,所以儘量減少對象的創建
    • 對象創建的少,將來垃圾收集器收集的垃圾也就少,提高效率
    • 對象創建的少,占用記憶體也就少,那麼剩餘的系統記憶體也就相對多,系統運行也就快
    • 避免在經常使用的方法中或迴圈中創建對象
  • 多個小的對象比大對象分配起來更加高效
    • 這是根據TLAB得出來的,多個小對象可以並行在各自的TLAB分配記憶體,而大對象的話,可能只能通過CAS同步來分配記憶體
  • 衡量上述兩點
  • 對於String
    • 儘量使用直接量:eg. String str = "hello";//常量會直接存在"常量池",而非String str = new String("hello");//除了將"hello"存在"常量池"之外,還會創建一個char[]
    • 不要使用String去拼接字元串,會形成許多臨時字元串:如下,
          String s1 = "hello1";
          String s2 = "hello2";
          String s3 = "hello3";
          String s4 = s1+s2+s3;
      View Code

      實際上,我們只想要字元串s1+s2+s3,但是在上述的拼接過程中,會形成s1+s2的臨時字元串。拼接字元串,使用StringBuilder,該類相較於StringBuffer由於不是同步類,其運行效果會更好。

    • 儘早釋放無用對象的引用(幫助垃圾回收)
          public void info(){
              Object obj = new Object();
              System.out.println(obj.hashCode());
              obj = null;//顯式釋放無用對象
          }
      View Code

      如上邊方法所示,其中的obj是一個局部變數,在方法執行結束後,棧幀就會出棧並被回收,棧幀中所存儲的局部變數一起被回收掉了,所以這裡的"obj=null;"就沒用了,但是看下邊

          public void info(){
              Object obj = new Object();
              System.out.println(obj.hashCode());
              obj = null;//顯式釋放無用對象
              //下邊還有一些很耗時、很耗記憶體的操作,這些操作與obj無關
          }
      View Code

      這時候,如果我們加上了"obj=null;"這一句,那麼就有可能在方法執行結束之前,obj被回收。

    • 儘量少使用static變數,因為static變數屬於類變數,存儲於方法區,其所占記憶體無法被垃圾回收器回收掉,除非static所屬的類被卸載掉。
  • 常用的對象放入緩存或連接池(其實,連接池也可以看做是一個緩存)
  • 考慮使用SoftReference(關於幾種引用方式,之後會說)
    • 當記憶體足夠時,功能等同於普通變數
    • 當記憶體不足時,釋放軟引用所引用的對象
    • 一般用於大數組、大對象
    • 通過軟引用所獲取的對象可能為null(當記憶體不足時,釋放軟引用所引用的對象),在應用程式中需要顯示判斷對象是否為null,若為null,需要重建對象

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在JDK1.0中,Date類是唯一的一個代表時間的類,但是由於Date類不便於實現國際化,所以從JDK1.1版本開始,推薦使用Calendar類進行時間和日期處理。 一、這裡簡單介紹一下Date類的使用。 1、使用Date類代表當前系統時間 Date d = new Date(); System.o
  • Widget Factory 題意:有n件裝飾品,有m組信息。(1 <= n ,m<= 300)每組信息有開始的星期和結束的星期(是在mod 7範圍內的)並且還包括num種裝飾品的種類(1~n),其中每種裝飾品所用的時間3 <= x[i] <= 9;種類的輸入可以重覆; 思路: 1.根據輸入建立增廣
  • Boss說,我們買了個權威證書,不如做全站式的https吧,讓用戶打開主頁就能看到受信任的綠標。於是我們就開始了填坑之旅。 【只上主域好不好?】 不好。。。console會報出一大堆warning因為圖片域沒有https~瀏覽器證書符號也不是綠色的~ 【在哪裡解密SSL?】 大網站都是架構複雜的啦~
  • 一、前言 本篇文章需要讀者有一點 Node.js 基礎的瞭解,並且已經安裝了 Node.js (node、npm),但並不需要有 Nokit 的知識,本文將簡單介紹 Nokitjs 的安裝使用,並編寫一個最簡單的 "Hello Word" 。 文中示例是在 Mac OSX 上完成的,整個步驟和 Li
  • 註意:本篇博客,主要參考自《深入理解Java虛擬機(第二版)》 1、對象在記憶體中存儲的佈局分為三塊 對象頭 存儲對象自身的運行時數據:Mark Word(在32bit和64bit虛擬機上長度分別為32bit和64bit),包含如下信息: 對象hashCode 對象GC分代年齡 鎖狀態標誌(輕量級鎖、
  • 如果給定一個list或tuple,我們可以通過for迴圈來遍歷這個list或tuple,這種遍歷我們稱為迭代(Iteration)。 在Python中,迭代是通過for ... in來完成的。 for key in d: print(key) 因為dict的存儲不是按照list的方式順序排列,所以,...
  • 以一元多項式加法運算為例: A,B可用線性鏈表可以表示為: “和多項式”鏈表如下(圖中的長方框表示已經被釋放的結點): #include <stdio.h> #include <stdlib.h> typedef struct Polyn{ int data; int index; struct P
  • Day14 集合框架01 體系概述02 共性方法03 迭代器04 List集合共性方法05 ListIterator06 List集合具體對象特點07 Vector中的枚舉 01 體系概述 集合類為什麼出現集合類?面向對象語言對事物的體現都是以對象的形式,所以為了方便對多個對象的操作,就需要對對象進
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...