乾貨:JVM 堆記憶體和非堆記憶體

来源:https://www.cnblogs.com/lfs2640666960/archive/2018/03/06/8516916.html
-Advertisement-
Play Games

堆和非堆記憶體 按照官方的說法:“Java 虛擬機具有一個堆(Heap),堆是運行時數據區域,所有類實例和數組的記憶體均從此處分配。堆是在 Java 虛擬機啟動時創建的。”“在JVM中堆之外的記憶體稱為非堆記憶體(Non-heap memory)”。 JVM主要管理兩種類型的記憶體:堆和非堆。 Heap me ...


堆和非堆記憶體

 

按照官方的說法:“Java 虛擬機具有一個堆(Heap),堆是運行時數據區域,所有類實例和數組的記憶體均從此處分配。堆是在 Java 虛擬機啟動時創建的。”“在JVM中堆之外的記憶體稱為非堆記憶體(Non-heap memory)”。

 

JVM主要管理兩種類型的記憶體:堆和非堆。

 

Heap memory Code Cache

Eden Space

Survivor Space

Tenured Gen

non-heap memory Perm Gen

native heap?(I guess)

 

堆記憶體

 

Java 虛擬機具有一個堆,堆是運行時數據區域,所有類實例和數組的記憶體均從此處分配。堆是在 Java 虛擬機啟動時創建的。對象的堆記憶體由稱為垃圾回收器的自動記憶體管理系統回收。

 

堆的大小可以固定,也可以擴大和縮小。堆的記憶體不需要是連續空間。

 

非堆記憶體

 

Java 虛擬機管理堆之外的記憶體(稱為非堆記憶體)。

 

Java 虛擬機具有一個由所有線程共用的方法區。方法區屬於非堆記憶體。它存儲每個類結構,如運行時常數池、欄位和方法數據,以及方法和構造方法的代碼。它是在 Java 虛擬機啟動時創建的。

 

方法區在邏輯上屬於堆,但 Java 虛擬機實現可以選擇不對其進行回收或壓縮。與堆類似,方法區的大小可以固定,也可以擴大和縮小。方法區的記憶體不需要是連續空間。

 

除了方法區外,Java 虛擬機實現可能需要用於內部處理或優化的記憶體,這種記憶體也是非堆記憶體。例如,JIT 編譯器需要記憶體來存儲從 Java 虛擬機代碼轉換而來的本機代碼,從而獲得高性能。

 

幾個基本概念

 

PermGen space:全稱是Permanent Generation space,即永久代。就是說是永久保存的區域,用於存放Class和Meta信息,Class在被Load的時候被放入該區域,GC(Garbage Collection)應該不會對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。

 

Heap space:存放Instance。

 

Java Heap分為3個區,Young即新生代,Old即老生代和Permanent。

 

Young保存剛實例化的對象。當該區被填滿時,GC會將對象移到Old區。Permanent區則負責保存反射對象。

 

堆記憶體分配

 

  • JVM初始分配的堆記憶體由-Xms指定,預設是物理記憶體的1/64;

  • JVM最大分配的堆記憶體由-Xmx指定,預設是物理記憶體的1/4。

  • 預設空餘堆記憶體小於40%時,JVM就會增大堆直到-Xmx的最大限制;

  • 空餘堆記憶體大於70%時,JVM會減少堆直到-Xms的最小限制。

  • 因此伺服器一般設置-Xms、-Xmx 相等以避免在每次GC 後調整堆的大小。

  • 說明:如果-Xmx 不指定或者指定偏小,應用可能會導致java.lang.OutOfMemory錯誤,此錯誤來自JVM,不是Throwable的,無法用try…catch捕捉。

 

非堆記憶體分配

 

1. JVM使用-XX:PermSize設置非堆記憶體初始值,預設是物理記憶體的1/64;

2. 由XX:MaxPermSize設置最大非堆記憶體的大小,預設是物理記憶體的1/4。

 

  • 還有一說:MaxPermSize預設值和-server -client選項相關,-server選項下預設MaxPermSize為64m,-client選項下預設MaxPermSize為32m。這個我沒有實驗。

 

3. XX:MaxPermSize設置過小會導致java.lang.OutOfMemoryError: PermGen space 就是記憶體益出。

4. 為什麼會記憶體益出:

 

  • 這一部分記憶體用於存放Class和Meta的信息,Class在被 Load的時候被放入PermGen space區域,它和存放Instance的Heap區域不同。

  • GC(Garbage Collection)不會在主程式運行期對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS 的話,就很可能出現PermGen space錯誤。

 

5. 這種錯誤常見在web伺服器對JSP進行pre compile的時候。

 

JVM記憶體限制(最大值)

 

1. 首先JVM記憶體限制於實際的最大物理記憶體,假設物理記憶體無限大的話,JVM記憶體的最大值跟操作系統有很大的關係。簡單的說就32位處理器雖然可控記憶體空間有4GB,但是具體的操作系統會給一個限制,這個限制一般是2GB-3GB(一般來說Windows系統下為1.5G-2G,Linux系統下為2G-3G),而64bit以上的處理器就不會有限制了。

 

2. 為什麼有的機器我將-Xmx和-XX:MaxPermSize都設置為512M之後Eclipse可以啟動,而有些機器無法啟動?

 

通過上面對JVM記憶體管理的介紹我們已經瞭解到JVM記憶體包含兩種:堆記憶體和非堆記憶體,另外JVM最大記憶體首先取決於實際的物理記憶體和操作系統。所以說設置VM參數導致程式無法啟動主要有以下幾種原因:

 

  • 參數中-Xms的值大於-Xmx,或者-XX:PermSize的值大於-XX:MaxPermSize;

  • -Xmx的值和-XX:MaxPermSize的總和超過了JVM記憶體的最大限制,比如當前操作系統最大記憶體限制,或者實際的物理記憶體等等。說到實際物理記憶體這裡需要說明一點的是,如果你的記憶體是1024MB,但實際系統中用到的並不可能是1024MB,因為有一部分被硬體占用了。

 

3. 如果你有一個雙核的CPU,也許可以嘗試這個參數: -XX:+UseParallelGC 讓GC可以更快的執行。(只是JDK 5里對GC新增加的參數)

 

4. 如果你的WEB APP下都用了大量的第三方jar,其大小超過了伺服器jvm預設的大小,那麼就會產生記憶體益出問題了。解決方法: 設置MaxPermSize大小。

 

  • 增加伺服器啟動的JVM參數設置: -Xms128m -Xmx256m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m

  • 如tomcat,修改TOMCAT_HOME/bin/catalina.sh,在echo “Using CATALINA_BASE: $CATALINA_BASE”上面加入以下行:JAVA_OPTS=”-server -XX:PermSize=64M -XX:MaxPermSize=128m

 

5. 建議:將相同的第三方jar文件移置到tomcat/shared/lib目錄下,這樣可以減少jar 文檔重覆占用記憶體

 

JVM記憶體設置參數

 

  • 記憶體設置參數

 

 

  • 說明:

  1. 如果-Xmx不指定或者指定偏小,應用可能會導致java.lang.OutOfMemory錯誤,此錯誤來自JVM不是Throwable的,無法用try…catch捕捉。

  2. PermSize和MaxPermSize指明虛擬機為java永久生成對象(Permanate generation)如,class對象、方法對象這些可反射(reflective)對象分配記憶體限制,這些記憶體不包括在Heap(堆記憶體)區之中。

  3. -XX:MaxPermSize分配過小會導致:java.lang.OutOfMemoryError: PermGen space。

  4. MaxPermSize預設值和-server -client選項相關:-server選項下預設MaxPermSize為64m、-client選項下預設MaxPermSize為32m。

 

  • 申請一塊記憶體的過程

 

  1. JVM會試圖為相關Java對象在Eden中初始化一塊記憶體區域

  2. 當Eden空間足夠時,記憶體申請結束。否則到下一步

  3. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收);釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區/OLD區

  4. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區

  5. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)

  6. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden複製過來的部分對象,導致JVM無法在Eden區為新對象創建記憶體區域,則出現”out of memory錯誤”

 

resin伺服器典型的響應時間優先型的jvm配置:

 

-Xmx2000M -Xms2000M -Xmn500M

-XX:PermSize=250M -XX:MaxPermSize=250M

-Xss256K

-XX:+DisableExplicitGC

-XX:SurvivorRatio=1

-XX:+UseConcMarkSweepGC

-XX:+UseParNewGC

-XX:+CMSParallelRemarkEnabled

-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction=0

-XX:+CMSClassUnloadingEnabled

-XX:LargePageSizeInBytes=128M

-XX:+UseFastAccessorMethods

-XX:+UseCMSInitiatingOccupancyOnly

-XX:CMSInitiatingOccupancyFraction=60

-XX:SoftRefLRUPolicyMSPerMB=0

-XX:+PrintClassHistogram

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-XX:+PrintHeapAtGC

-Xloggc:log/gc.log

 

記憶體回收演算法

 

Java中有四種不同的回收演算法,對應的啟動參數為:

 

–XX:+UseSerialGC

–XX:+UseParallelGC

–XX:+UseParallelOldGC

–XX:+UseConcMarkSweepGC

 

Serial Collector

 

大部分平臺或者強制 java -client 預設會使用這種。

 

young generation演算法 = serial

old generation演算法 = serial (mark-sweep-compact)

 

這種方法的缺點很明顯, stop-the-world, 速度慢。伺服器應用不推薦使用。

 

Parallel Collector

 

在linux x64上預設是這種,其他平臺要加 java -server 參數才會預設選用這種。

 

young = parallel,多個thread同時copy

old = mark-sweep-compact = 1

 

優點:新生代回收更快。因為系統大部分時間做的gc都是新生代的,這樣提高了throughput(cpu用於非gc時間)

 

缺點:當運行在8G/16G server上old generation live object太多時候pause time過長

 

Parallel Compact Collector (ParallelOld)

 

young = parallel = 2

 

old = parallel,分成多個獨立的單元,如果單元中live object少則回收,多則跳過

 

優點:old old generation上性能較 parallel 方式有提高

 

缺點:大部分server系統old generation記憶體占用會達到60%-80%, 沒有那麼多理想的單元live object很少方便迅速回收,同時compact方面開銷比起parallel並沒明顯減少。

 

Concurrent Mark-Sweep(CMS) Collector

 

young generation = parallel collector = 2

old = cms

 

同時不做 compact 操作。

 

優點:pause time會降低, pause敏感但CPU有空閑的場景需要建議使用策略4.

 

缺點:cpu占用過多,cpu密集型伺服器不適合。另外碎片太多,每個object的存儲都要通過鏈表連續跳n個地方,空間浪費問題也會增大。

 

記憶體監控方法

 

  • jmap -heap 查看java 堆(heap)使用情況

 

jmap -heap pid 

  

using thread-local object allocation.

  

Parallel GC with 4 thread(s)   #GC 方式

  

Heap Configuration:  #堆記憶體初始化配置

  

MinHeapFreeRatio=40  #對應jvm啟動參數-XX:MinHeapFreeRatio設置JVM堆最小空閑比率(default 40)

MaxHeapFreeRatio=70  #對應jvm啟動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閑比率(default 70)

MaxHeapSize=512.0MB  #對應jvm啟動參數-XX:MaxHeapSize=設置JVM堆的最大大小

NewSize  = 1.0MB     #對應jvm啟動參數-XX:NewSize=設置JVM堆的‘新生代’的預設大小

MaxNewSize =4095MB   #對應jvm啟動參數-XX:MaxNewSize=設置JVM堆的‘新生代’的最大大小

OldSize  = 4.0MB     #對應jvm啟動參數-XX:OldSize=<value>:設置JVM堆的‘老生代’的大小

NewRatio  = 8        #對應jvm啟動參數-XX:NewRatio=:‘新生代’和‘老生代’的大小比率

SurvivorRatio = 8    #對應jvm啟動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值

PermSize= 16.0MB     #對應jvm啟動參數-XX:PermSize=<value>:設置JVM堆的‘永生代’的初始大小

MaxPermSize=64.0MB   #對應jvm啟動參數-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的最大大小

  

Heap Usage:          #堆記憶體分步

  

PS Young Generation

  

Eden Space:         #Eden區記憶體分佈

  

capacity = 20381696 (19.4375MB)             #Eden區總容量

used     = 20370032 (19.426376342773438MB)  #Eden區已使用

free     = 11664 (0.0111236572265625MB)     #Eden區剩餘容量

99.94277218147106% used                     #Eden區使用比率

  

From Space:        #其中一個Survivor區的記憶體分佈

  

capacity = 8519680 (8.125MB)

used     = 32768 (0.03125MB)

free     = 8486912 (8.09375MB)

0.38461538461538464% used

  

To Space:          #另一個Survivor區的記憶體分佈

  

capacity = 9306112 (8.875MB)

used     = 0 (0.0MB)

free     = 9306112 (8.875MB)

0.0% used

  

PS Old Generation  #當前的Old區記憶體分佈

  

capacity = 366280704 (349.3125MB)

used     = 322179848 (307.25464630126953MB)

free     = 44100856 (42.05785369873047MB)

87.95982001825573% used

  

PS Perm Generation #當前的 “永生代” 記憶體分佈

  

capacity = 32243712 (30.75MB)

used     = 28918584 (27.57891082763672MB)

free     = 3325128 (3.1710891723632812MB)

89.68751488662348% used

 

  • JVM記憶體監控工具

 

<%@ page import="java.lang.management.*" %>

<%@ page import="java.util.*" %>

<html>

<head>

  <title>JVM Memory Monitor</title>

</head>

<body>

<table border="0" width="100%">

    <tr><td colspan="2" align="center"><h3>Memory MXBean</h3></td></tr>

    <tr><td width="200">Heap Memory Usage</td><td><%=ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()%></td></tr>

    <tr><td>Non-Heap Memory Usage</td><td><%=ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()%></td></tr>

    <tr><td colspan="2"> </td></tr>

    <tr><td colspan="2" align="center"><h3>Memory Pool MXBeans</h3></td></tr>

<%

        Iterator iter = ManagementFactory.getMemoryPoolMXBeans().iterator();

        while (iter.hasNext()) {

            MemoryPoolMXBean item = (MemoryPoolMXBean) iter.next();

%>

<tr><td colspan="2">

    <table border="0" width="100%" style="border: 1px #98AAB1 solid;">

        <tr><td colspan="2" align="center"><b><%= item.getName() %></b></td></tr>

        <tr><td width="200">Type</td><td><%= item.getType() %></td></tr>

        <tr><td>Usage</td><td><%= item.getUsage() %></td></tr>

        <tr><td>Peak Usage</td><td><%= item.getPeakUsage() %></td></tr>

        <tr><td>Collection Usage</td><td><%= item.getCollectionUsage() %></td></tr>

    </table>

</td></tr>

<tr><td colspan="2"> </td></tr>

<%} %>

</table>

</body>

</html>

推薦一個交流學習群:650385180裡面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分散式、微服務架構的原理,JVM性能優化這些成為架構師必備的知識體系。還能領取免費的學習資源,目前受益良多:


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

-Advertisement-
Play Games
更多相關文章
  • 第四章 模板 1.標簽 (1)if/else {% if %} 標簽檢查(evaluate)一個變數,如果這個變數為真(即,變數存在,非空,不是布爾值假),系統會顯示在 {% if %} 和 {% endif %} 之間的任何內容,例如: {% else %} 標簽是可選的: {% if %} 標簽 ...
  • 一、前言 最近公司有使用阿裡雲消息隊列的需求,為了更加方便使用,本人用了幾天時間將消息隊列封裝成api調用方式以方便內部系統的調用,現在已經完成,特此記錄其中過程和使用到的相關技術,與君共勉。 現在阿裡雲提供了兩種消息服務:mns服務和ons服務,其中我認為mns是簡化版的ons,而且mns的消息消 ...
  • 1. CentOS7安裝MySQL # wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm # rpm -ivh mysql-community-release-el7-5.noarch.rpm # yum i ...
  • Description 給定兩個集合A和B的所有元素,計算它們的交、並、差集。 Input 輸入數據有多組,第一行為數據的組數T,接下來有2T行,每組數據占2行,每行有若幹個整數,第一行的所有整數構成集合A,第二行的所有整數構成集合B,分別用空格分隔。A和B最多分別不超過100個元素。 Output ...
  • 對象(四) 一、封裝 面向對象的三大特征:封裝、繼承、多態。 今天呢,我們來談談,其中兩個 封裝和繼承。而多態呢,如果沒有繼承也就沒有多態一說,這個我們後續繼續聊。 隱藏了實現細節,提供公共的訪問方式 提高了代碼復用性 提高安全性 隱藏了實現細節,提供公共的訪問方式 提高了代碼復用性 提高安全性 3 ...
  • 引言 本來計劃每周完成一篇Python的自學博客,由於上一篇到這一篇遇到了過年、開學等雜事,導致托更到現在。現在又是一個新的學期,春天也越來越近了(冷到感冒)。好了,閑話就說這麼多。開始本周的自學Python之路。而且,同時從這周開始,也要開始自學Tensorflow。希望能嚴格要求自己,不會托更。 ...
  • 1101: [POI2007]Zap Description FGD正在破解一段密碼,他需要回答很多類似的問題:對於給定的整數a,b和d,有多少正整數對x,y,滿足x<=a,y<=b,並且gcd(x,y)=d。作為FGD的同學,FGD希望得到你的幫助。 FGD正在破解一段密碼,他需要回答很多類似的問 ...
  • 1、 Iterable 與 Iterator Iterable 是個介面,實現此介面使集合對象可以通過迭代器遍歷自身元素. public interface Iterable<T> 第一個介面iterator()是jdk1.5引入的,需要子類實現一個內部迭代器Iterator遍歷元素。 後兩個介面是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...