java虛擬機總結

来源:http://www.cnblogs.com/wmbg/archive/2017/05/08/6823468.html
-Advertisement-
Play Games

jvm記憶體模型 u 程式計數器 u Java棧(虛擬機棧) u 本地方法棧 u Java堆 u 方法區及其運行時常量池 垃圾回收機制 u 新生代和老年代 u 參數設置 u 垃圾回收(Minor GC 和 Full GC)和回收演算法 u finalize()、減少GC開銷、觸發主GC的條件 u 判斷無 ...


jvm記憶體模型

程式計數器

Java(虛擬機棧)

本地方法棧

Java

方法區及其運行時常量池

垃圾回收機制

u  新生代和老年代

u  參數設置

u  垃圾回收(Minor GC 和 Full GC)和回收演算法

u  finalize()、減少GC開銷、觸發主GC的條件

u  判斷無用對象、四種引用方式、為什麼進行垃圾回收

u  String、StringBuffer與StringBuilder

u  類載入機制

類載入機制

一 : jvm記憶體模型

 

第一、程式計數器(PC)

程式計數器(Program Counter Register)是一塊較小的記憶體空間,它可以看做當前線程所執行的位元組碼的行號指示器,位元組碼解釋器工作時就是通過改變這個計數器的值來取下一條需要執行的位元組碼指令

由於Java 虛擬機的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對於多核處理器來說是一個內核)只會執行一條線程中的指令。因此,為了線程切換後能恢復到正確的執行位置,每條線程都需要有一個獨立的程式計數器,各條線程之間的計數器互不影響,獨立存儲,我們稱這類記憶體區域為“線程私有”的記憶體。

如果線程正在執行的是一個Java 方法,這個計數器記錄的是正在執行的虛擬機位元組碼指令的地址;如果正在執行的是Natvie 方法,這個計數器值則為空(Undefined)。

註:程式計數器是線程私有的,每條線程都會有一個獨立的程式計數器

第二、Java棧(虛擬機棧)

Java棧就是Java中的方法執行的記憶體模型,每個方法在執行的同時都會創建一個棧幀(關於棧幀後面介紹),這個棧幀用於存儲局部變數表、操作數棧、動態鏈接、方法出口等信息,每個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。

註:Java棧也是線程私有的。

異常可能性:對於棧有兩種異常情況:如果線程請求的棧深度大於棧所允許的深度,將拋出StackOverflowError異常,如果虛擬機棧可以動態拓展,在拓展的時無法申請到足夠的記憶體,將會拋出OutOfMemoryError異常

棧幀

1)  局部變數表

2)  操作數棧

3)  整數加法

4)  法返回地址

第三、本地方法棧

本地方法棧與Java棧所發揮的作用是非常相似的,它們之間的區別不過是Java棧執行Java方法,本地方法棧執行的是本地方法。

註:本地方法棧也是線程私有的

異常可能性:和Java棧一樣,可能拋出StackOverflowError和OutOfMemeryError異常

第四、Java堆

對於大多數應用來說,Java堆是Java虛擬機所管理的記憶體中最大的一塊,在虛擬機啟動時創建。此記憶體區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裡分配記憶體,當然我們後面說到的垃圾回收器的內容的時候,其實Java堆就是垃圾回收器管理的主要區域。

註:堆是線程共用的

異常可能性:如果堆中沒有記憶體完成實例分配,並且堆也無法再拓展時,將會拋出OutOfMemeryError異常

第五、方法區(如果一個系統不斷地產生新的類,而沒有回收,那最終非常有可能導致永久區溢出。)

方法區它用於存儲已被虛擬機載入的類信息(類型信息,欄位信息,方法信息,其他信息)靜態量、即時編譯器編譯後的代碼等數據。方法區是線程安全的

註:方法區和堆一樣是線程共用的

異常可能性:當方法區無法滿足記憶體分配需求時,將拋出OutOfMemeryError異常

運行時常量池

用於存放編譯器生成的各種字面量和符號引用

參數配置

  • -Xmx3550m:設置JVM最大堆記憶體為3550M。
  • -Xms3550m:設置JVM初始堆記憶體為3550M。
  • -XX:NewSize=1024m:設置新生代初始值為1024M。
  • -XX:MaxNewSize=1024m:設置新生代最大值為1024M。
  • -XX:PermSize=256m:設置老年代初始值為256M。
  • -XX:MaxPermSize=256m:設置老年代最大值為256M。
  • -XX:NewRatio=4:設置年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示新生代比老年代為1:4。
  • -XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的比值。表示2個Survivor區與1個Eden區的比值為2:4,即1個Survivor區占整個年輕代大小的1/6。

二 :Jvm垃圾回收

Java的垃圾回收機制是Java虛擬機提供的能力,用於在空閑時間以不定時的方式動態回收無任何引用的對象占據的記憶體空間。

堆的記憶體分配

新生代(Young Generation)(預設的Eden:Survivor = 8:1)

  1.所有新生成的對象首先都是放在新生代的。新生代的目標就是儘可能快速的收集掉那些生命周期短的對象。

  2.新生代記憶體按照8:1:1的比例分為一個eden區和兩個survivor(survivor0,survivor1)區。一個Eden區,兩個 Survivor區(一般而言)。大部分對象在Eden區中生成。回收時先將eden區存活對象複製到一個survivor0區,然後清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活對象複製到另一個survivor1區,然後清空eden和這個survivor0區,此時survivor0區是空的,然後將survivor0區和survivor1區交換,即保持survivor1區為空, 如此往複。

  3.當survivor1區不足以存放 eden和survivor0的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發一次Full GC,也就是新生代、老年代都進行回收

4.新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)

在Minor GC時會將新生代中還存活著的對象複製進一個Survivor中,然後對Eden和另一個Survivor進行清理。所以,平常可用的新生代大小為Eden的大小+一個Survivor的大小。所有的Minor GC都會觸發全世界的暫停(stop-the-world除了垃圾收集收集器線程之外的線程都被掛起),停止應用程式的線程,不過這個過程非常短暫。Eden 和Survivor區不存在記憶體碎片。

 

當對象在 Eden出生後,在經過一次 Minor GC 後,如果對象還存活,並且能夠被另外一塊 Survivor 區域所容納上面已經假設為 from 區域,這裡應為 to 區域,即 to 區域有足夠的記憶體空間來存儲 Eden 和 from 區域中存活的對象 ),則使用複製演算法將這些仍然還存活的對象複製到另外一塊 Survivor 區域 ( to 區域 ) ,然後清理所使用過的 Eden 以及 Survivor 區域 ( 即from 區域 ),並且將這些對象的年齡設置為1,以後對象在 Survivor 區每熬過一次 Minor GC,就將對象的年齡 + 1,當對象的年齡達到某個值時 ( 預設是 15 歲,可以通過參數 -XX:MaxTenuringThreshold 來設定),這些對象就會成為老年代。

對於一些較大的對象即需要分配一塊較大的連續記憶體空間 ) 則是直接進入到老年代

 

在執行機制上JVM提供了串列GC(SerialGC)、並行回收GC(ParallelScavenge)和並行GC(ParNew)

年老代(Old Generation)

  1 . 在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。

  2 . 記憶體比新生代也大很多(大概比例是1:2),當老年代記憶體滿時觸發Full GC  發生頻率比較低,老年代對象存活時間比較長,存活率標記高。

什麼情況下,新生代的對象會進入老年代呢?

當Minor GC時,新生代存活的對象大於Survivor的大小時,這時一個Survivor裝不下它們,那麼它們就會進入老年代。

在新生代的每一次Minor GC 都會給在新生代中的對象+1歲,預設到15歲時就會從新生代進入老年代-XX:MaxTenuringThreshold來設置這個臨界點。

如果設置了-XX:PretenureSizeThreshold3M 那麼大於3M的對象就會直接就進入老年代。

3.finalize()方法什麼時候被調用?

l  垃圾回收器(garbage colector)決定回收某對象時,就會運行該對象的finalize()方法 但是在Java中很不幸,如果記憶體總是充足的,那麼垃圾回收可能永遠不會進行,也就是說filalize()可能永遠不被執行,顯然指望它做收尾工作是靠不住的。 那麼finalize()究竟是做什麼的呢?

l  它最主要的用途是回收特殊渠道申請的記憶體。由於垃圾回收器只知道那些顯示地經由new分配的記憶體空間,所以它不知道該如何釋放這塊“特殊”的記憶體區域,那麼這個時候java允許在類中定義一個由 finalize()方法

3.減少GC開銷的措施

(1)不要顯式調用System.gc()

(2)儘量減少臨時對象的使用

(3)對象不用時最好顯式置為Null

(4)儘量使用StringBuffer,而不用String來累加字元串

 

2.觸發主GC(Garbage Collector)的條件

(1)當應用程式空閑時,即沒有應用線程在運行時,GC會被調用。因為GC在優先順序最低的線程中進行,所以當應用忙時,GC線程就不會被調用,但以下條件除外。

(2)Java堆記憶體不足時,GC會被調用。

一.如何確定某個對象是“垃圾”(無用對象)?

引用計數法

如果一個對象沒有任何引用與之關聯,則說明該對象基本不太可能在其他地方被使用到,那麼這個對象就成為可被回收的對象了。迴圈引用對象將無法回收

可達性分析法

該方法的基本思想是通過一系列的根對象的集合作為起點進行搜索,從這些根對象開始,任何可以被觸及的對象都是被認為是“活動”的對象。無法被觸及的對象被認為是垃圾,因為它們不在影響程式的未來執行。

二.典型的垃圾收集演算法

1.Mark-Sweep(標記-清除)演算法

這是最基礎的垃圾回收演算法,之所以說它是最基礎的是因為它最容易實現,思想也是最簡單的。標記-清除演算法分為兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所占用的空間。

從圖中可以很容易看出標記-清除演算法實現起來比較容易,但是有一個比較嚴重的問題就是容易產生記憶體碎片,碎片太多可能會導致後續過程中需要為大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。

2.Copying(複製)演算法

為瞭解決Mark-Sweep演算法的缺陷,Copying演算法就被提了出來。它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的對象複製到另外一塊上面,然後再把已使用的記憶體空間一次清理掉,這樣一來就不容易出現記憶體碎片的問題。問題是占用記憶體較多

3.Mark-Compact(標記-整理)演算法

為瞭解決Copying演算法的缺陷,充分利用記憶體空間,提出了Mark-Compact演算法。該演算法標記階段和Mark-Sweep一樣,但是在完成標記之後,它不是直接清理可回收對象,而是將存活對象都向一端移動,然後清理掉端邊界以外的記憶體。

4.分代收集演算法

一般是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。在新生代中,每次垃圾收集時都發現有大批對象死去,那就選用複製演算法,只需要付出少量存活對象的複製成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清理”或“標記-整理”演算法來進行回收。方法區永久代,回收方法同老年代。

 

老年代採用的是標記-清除或者標記-整理演算法,這兩個演算法主要看虛擬機採用的哪個收集器,兩種演算法的區別是:標記-清除可能會產生大量連續的記憶體碎片。

三.String、StringBuffer與StringBuilder之間區別

String 字元串常亮,底層是數組實現的

StringBuilder:線程非安全的

StringBuffer:線程安全的

對於三者使用的總結:

1.如果要操作少量的數據用 = String

  2.單線程操作字元串緩衝區下操作大量數據 = StringBuilder

3.多線程操作字元串緩衝區下操作大量數據 = StringBuffer

四.為什麼要進行垃圾回收

在C++中,對象所占的記憶體在程式結束運行之前一直被占用,在明確釋放之前不能分配給其它對象;而在Java中,當沒有對象引用指向原先分配給某個對象的記憶體時,該記憶體便成為垃圾。 JVM的一個系統級線程會自動釋放該記憶體塊,減輕編程的負擔。事實上,除了釋放沒用的對象,垃圾回收也可以清除記憶體記錄碎片。

五.java虛擬機類載入機制

其中類載入的過程包括了載入、驗證、準備、解析、初始化五個階段。

l  載入

    載入時類載入過程的第一個階段,在載入階段,虛擬機需要完成以下三件事情:

    1、通過一個類的全限定名來獲取其定義的二進位位元組流。

    2、將這個位元組流所代表的靜態存儲結構轉化為方法區的運行時數據結構

3、在Java堆中生成一個代表這個類的java.lang.Class對象,作為對方法區中這些數據的訪問入口。

對於任意一個類,都需要由它的類載入器和這個類本身一同確定其在就Java虛擬機中的唯一性

類載入器:啟動類載入器、擴展類載入器、應用程式類載入器、自定義類載入器

雙親委派模型:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把請求委托給父載入器去完成,依次向上,因此,所有的類載入請求最終都應該被傳遞到頂層的啟動類載入器中,只有當父載入器在它的搜索範圍中沒有找到所需的類時,即無法完成該載入,子載入器才會嘗試自己去載入該類。

l  驗證

驗證的目的是為了確保Class文件中的位元組流包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。

l  準備

準備階段是正式為類變數分配記憶體並設置類變數初始值(零)的階段,這些記憶體都將在方法區中分配。

l  解析

解析階段是虛擬機將常量池中的符號引用轉化為直接引用的過程。

l  初始化

初始化是類載入過程的最後一步,此階段才開始真正執行類中定義的Java程式代碼(靜態語句塊和構造器)

四.JAVA四種引用方式

強引用、軟引用、弱引用、虛引用

l  強引用:當我們使用new 這個關鍵字創建對象時被創建的對象就是強引用,垃圾回收器就不會去回收有強引用的對象。

l  軟引用:如果一個對象具備軟引用,如果記憶體空間足夠,那麼垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收該對象。當然沒有被回收之前,該對象依然可以被程式調用。java.lang.ref.SoftReference

l  弱引用:如果一個對象只具有弱引用,只要垃圾回收器在自己的記憶體空間中線程檢測到了,就會立即被回收,對應記憶體也會被釋放掉。java.lang.ref.WeakReference

l  虛引用:如果一個對象只具有虛引用,那麼它就和沒有任何引用一樣,隨時會被jvm當作垃圾進行回收。虛引用目的:當對象被收集器回收時收到系統通知。java.lang.ref.PhantomReference

 


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

-Advertisement-
Play Games
更多相關文章
  • linux 2cpu 16g記憶體 ,tomcat7 JAVA_OPTS="-Xmx1024m -Xms1024m -Xss1024K -XX:PermSize=512m -XX:MaxPermSize=1024m"cygwin=false 堆記憶體: xmx,xms最大為物理記憶體的1/4,同樣大小,省 ...
  • 首先,安裝一下STS阿裡雲的jar包,官方文檔詳情在https://help.aliyun.com/document_detail/28788.html?spm=5176.doc28789.6.704.dasE9X,jar包地址https://docs-aliyun.cn-hangzhou.oss. ...
  • Implement strStr(). Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. ...
  • 看了好多關於代理的文章,理解和整理一下。 1、代理的基本構成 抽象角色:聲明真實對象和代理對象的共同介面,這樣可在任何使用真實對象的地方都可以使用代理對象。 代理角色:代理對象內部含有真實對象的引用,從而可以在任何時候操作真實對象。代理對象提供一個與真實對象相同的介面,以便可以在任何時候替代真實對象 ...
  • Description Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. Do not allocate ex ...
  • Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k is a positive integer and is less than or equal t... ...
  • 首先瞭解一下Python的優點: Python為我們提供了非常完善的基礎代碼庫,覆蓋了網路、文件、GUI、資料庫、文本等大量內容,被形象地稱作“內置電池(batteries included)”。用Python開發,許多功能不必從零編寫,直接使用現成的即可。 除了內置的庫外,Python還有大量的第 ...
  • 上篇博客我們聊了《JavaEE開發之SpringBoot工程的創建、運行與配置》,從上篇博客的內容我們不難看出SpringBoot的便捷。本篇博客我們繼續在上篇博客的基礎上來看一下SpringBoot是如何引入和使用MyBatis和Thymeleaf的。在之前的博客中我們提到過Hibernate,今 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...