1. 程式人生 > >JVM記憶體設定,各個引數含義和設定後不起作用問題

JVM記憶體設定,各個引數含義和設定後不起作用問題

這兩天才來公司,遇到很棘手的問題,把公司專案下載下來後無法執行,報錯記憶體溢位問題,然後網上一大堆問題的答案,按照網上的答案解決了tomcat:java.lang.OutOfMemoryError: PermGen space問題,但是後面還有tomcat:java.lang.OutOfMemoryError: Java heap space問題。按照網上的說法設定並沒有解決我的問題,於是開始太多JVM記憶體機制。在這裡終結一下怎樣解決的問題,希望能幫助自己和他人。如有錯誤,請大佬指出。

首先來看一下JVM記憶體有哪些區域,主要的作用是什麼?(摘自http://blog.csdn.net/zhoudaxia/article/details/26454421/)

執行時資料區(Runtime Data Areas)

圖 4: 執行時資料區

  執行時資料區是在JVM執行的時候作業系統所分配的記憶體區。執行時記憶體區可以劃分為6個區域。在這6個區域中,一個PC Register,JVM stack 以及Native Method Statck都是按照執行緒建立的,Heap,Method Area以及Runtime Constant Pool都是被所有執行緒公用的。

  •  PC暫存器(PC register):每個執行緒啟動的時候,都會建立一個PC(Program Counter,程式計數器)暫存器。PC暫存器裡儲存有當前正在執行的JVM指令的地址。
  • JVM 堆疊(JVM stack):每個執行緒啟動的時候,都會建立一個JVM堆疊。它是用來儲存棧幀的。JVM只會在JVM堆疊上對棧幀進行push和pop的操作。如果出現了異常,堆疊跟蹤資訊的每一行都代表一個棧幀立的資訊,這些資訊是通過類似於printStackTrace()這樣的方法來展示的。

圖 5: JVM堆疊

  --- 棧幀(stack frame):每當一個方法在JVM上執行的時候,都會建立一個棧幀,並且會新增到當前執行緒的JVM堆疊上。當這個方法執行結束的時候,這個棧幀就會被移除。每個棧幀裡都包含有當前正在執行的方法所屬類的本地變數陣列,運算元棧,以及執行時常量池的引用。本地變數陣列的和運算元棧的大小都是在編譯時確定的。因此,一個方法的棧幀的大小也是固定不變的。

--- 區域性變數陣列(Local variable array):這個陣列的索引從0開始。索引為0的變量表示這個方法所屬的類的例項。從1開始,首先存放的是傳給該方法的引數,在引數後面儲存的是方法的區域性變數。

  --- 運算元棧(Operand stack):方法實際執行的工作空間。每個方法都在運算元棧和區域性變數陣列之間交換資料,並且壓入或者彈出其他方法返回的結果。運算元棧所需的最大空間是在編譯期確定的。因此,運算元棧的大小也可以在編譯期間確定。

  •  本地方法棧(Native method stack):供用非Java語言實現的本地方法的堆疊。換句話說,它是用來呼叫通過JNI(Java Native Interface Java本地介面)呼叫的C/C++程式碼。根據具體的語言,一個C堆疊或者C++堆疊會被建立。
  •  方法區(Method area):方法區是所有執行緒共享的,它是在JVM啟動的時候建立的。它儲存所有被JVM載入的類和介面的執行時常量池,成員變數以及方法的資訊,靜態變數以及方法的位元組碼。JVM的提供者可以通過不同的方式來實現方法區。在Oracle 的HotSpot JVM裡,方法區被稱為永久區或者永久代(PermGen)。是否對方法區進行垃圾回收對JVM的實現是可選的。
  •   執行時常量池(Runtime constant pool):這個區域和class檔案裡的constant_pool是相對應的。這個區域是包含在方法區裡的,不過,對於JVM的操作而言,它是一個核心的角色。因此在JVM規範裡特別提到了它的重要性。除了包含每個類和介面的常量,它也包含了所有方法和變數的引用。簡而言之,當一個方法或者變數被引用時,JVM通過執行時常量區來查詢方法或者變數在記憶體裡的實際地址。
  • 堆(Heap):用來儲存例項或者物件的空間,而且它是垃圾回收的主要目標。當討論類似於JVM效能之類的問題時,它經常會被提及。JVM提供者可以決定怎麼來配置堆空間,以及不對它進行垃圾回收。

我這裡的異常tomcat:java.lang.OutOfMemoryError: Java heap space是堆空間不足,接著來看設定JVM的引數和各個引數的含義。(摘自:http://blog.csdn.net/shenhonglei1234/article/details/54950663)

上圖中,刻畫了Java程式執行時的堆空間,可以簡述成如下2條

1.JVM中堆空間可以分成三個大區,新生代、老年代、永久代

2.新生代可以劃分為三個區,Eden區,兩個倖存區

在JVM執行時,可以通過配置以下引數改變整個JVM堆的配置比例

1.JVM執行時堆的大小

  -Xms堆的最小值

  -Xmx堆空間的最大值

2.新生代堆空間大小調整

  -XX:NewSize新生代的最小值

  -XX:MaxNewSize新生代的最大值

  -XX:NewRatio設定新生代與老年代在堆空間的大小

  -XX:SurvivorRatio新生代中Eden所佔區域的大小

3.永久代大小調整

  -XX:MaxPermSize

4.其他

   -XX:MaxTenuringThreshold,設定將新生代物件轉到老年代時需要經過多少次垃圾回收,但是仍然沒有被回收

接著我在myeclipse.ini配置檔案中配置引數(公司老員工給我的配置引數)

-Xms1024m

-Xmx2048m

-XX:MaxPermSize=512m

-XX:ReservedCodeCacheSize=64m

重啟專案,然而還是tomcat:java.lang.OutOfMemoryError: Java heap space問題,很鬱悶,應該是對的啊。最終我使用jdk裡面的工具(jdk安裝目錄下bin中的)jvisualvm工具(直接雙擊,前提是tomcat執行狀態,才能選擇連線到tomcat),連線tomcat後檢視記憶體引數,發現我的虛擬機器只有256M的記憶體(下圖是改了過後,有1581252608b)。不應該啊,我明明配置了的啊!!

然後我把-Xmx2048m改成了-Xmx1024m,自從執行就好了,他就好了,很少驚訝,不知道為什麼,但是最後發現只要我的-Xms和-Xmx引數設定為一樣大小就會起作用,具體原因不詳