1. 程式人生 > >jvm 調優

jvm 調優

最大值 出口 manage 關聯關系 vivo 加載 count 速度慢 lvm

  Java虛擬機在執行Java程序的過程中會把它所管理的內存劃分為若幹個不同的數據區域。這些區域都有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而存在,有些區域則是依賴用戶線程的啟動和結束而建立和銷毀。根據《Java虛擬機規範(第2版)》的規定,Java虛擬機所管理的內存將會包括以下幾個運行時數據區域,虛擬機棧、本地方法棧、程序計數器為線程私有部分,虛擬機堆、方法區為共享部分),見下圖:

技術分享

程序計數器     

  程序計數器(Program Counter Register)是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。

Java虛擬機棧

  它的生命周期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用於存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。

  “棧”就是現在講的虛擬機棧,或者說是虛擬機棧中的局部變量表部分。局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型),它不等同於對象本身,根據不同的虛擬機實現,它可能是一個指向對象起始地址的引用指針,也可能指向一個代表對象的句柄或者其他與此對象相關的位置)和returnAddress類型(指向了一條字節碼指令的地址)。

  局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

Java堆

  Java堆(Java Heap)是Java虛擬機所管理的內存中最大的一塊。Java堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。這一點在Java虛擬機規範中的描述是:所有的對象實例以及數組都要在堆上分配,Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱做“GC堆”(Garbage Collected Heap,幸好國內沒翻譯成“垃圾堆”)。如果從內存回收的角度看,由於現在收集器基本都是采用的分代收集算法,所以Java堆中還可以細分為:新生代和老年代;再細致一點的有Eden空間、From Survivor空間、To Survivor空間等。Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可。

方法區

  方法區(Method Area)與Java堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

對象訪問

  介紹完Java虛擬機的運行時數據區之後,我們就可以來探討一個問題:在Java語言中,對象訪問是如何進行的?對象訪問在Java語言中無處不在,是最普通的程序行為,但即使是最簡單的訪問,也會卻涉及Java棧、Java堆、方法區這三個最重要內存區域之間的關聯關系,如下面的這句代碼:Object obj = new Object();

  假設這句代碼出現在方法體中,那“Object obj”這部分的語義將會反映到Java棧的本地變量表中,作為一個reference類型數據出現。

  而“new Object()”這部分的語義將會反映到Java堆中,形成一塊存儲了Object類型所有實例數據值(Instance Data,對象中各個實例字段的數據)的結構化內存,根據具體類型以及虛擬機實現的對象內存布局(Object Memory Layout)的不同,這塊內存的長度是不固定的。

  另外,在Java堆中還必須包含能查找到此對象類型數據(如對象類型、父類、實現的接口、方法等)的地址信息,這些類型數據則存儲在方法區中。


  以上是java內存的簡單介紹,下面根據內存模型介紹jvm調優。

  工具:jdk自帶的jvisualvm.exe,壓測軟件apache-jmeter-3.2

整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小。 Heap = {Old + NEW = { Eden , from, to }+perm }

   -Xmx Java Heap最大值,默認值為物理內存的1/4,最佳設值視物理內存大小及計算機內其他內存開銷而定

  -Xms Java Heap初始值,Server端JVM最好將-Xms和-Xmx設為相同值,以避免每次垃圾回收完成後JVM重新分配內存,開發測試機JVM可以保留默認值;

  -Xmn Java Heap Young區大小,持久代一般固定大小為64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。

  -Xss 每個線程的Stack大小

  -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5

  -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5

  

  垃圾回收描述:

  在New Generation塊中,垃圾回收一般用Copying的算法,速度快。每次GC的時候,存活下來的對象首先由Eden拷貝到某個Survivor Space, 當Survivor Space空間滿了後, 剩下的live對象就被直接拷貝到Old Generation中去。因此,每次GC後,Eden內存塊會被清空。在Old Generation塊中,垃圾回收一般用mark-compact的算法,速度慢些,但減少內存要求.

  當一個URL被訪問時,內存申請過程如下:
  A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域
  B. 當Eden空間足夠時,內存申請結束。否則到下一步
  C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收), 釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區
  D. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
  E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
  F. 完全垃圾收集後,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現”out of memory錯誤”

  

  JVM調優建議:

  ms/mx:定義YOUNG+OLD段的總尺寸,ms為JVM啟動時YOUNG+OLD的內存大小;mx為最大可占用的YOUNG+OLD內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
  NewSize/MaxNewSize:定義YOUNG段的尺寸,NewSize為JVM啟動時YOUNG的內存大小;MaxNewSize為最大可占用的YOUNG內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
  PermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可占用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。

  如果你發現每次GC後,Heap的剩余空間會是總空間的50%,這表示你的Heap處於健康狀態。許多Server端的Java程序每次GC後最好能有65%的剩余空間。一個GUI程序最好是每10到20秒間運行一次GC,每次在半秒之內完成[2]。

1.增加Heap的大小雖然會降低GC的頻率,但也增加了每次GC的時間。並且GC運行時,所有的用戶線程將暫停,也就是GC期間,Java應用程序不做任何工作。

2.Heap大小並不決定進程的內存使用量。進程的內存使用量要大於-Xmx定義的值,因為Java為其他任務分配內存,例如每個線程的Stack等。

3.Stack的設定

每個線程都有他自己的Stack。

Stack的大小限制著線程的數量。如果Stack過大就好導致內存溢漏。-Xss參數決定Stack大小,例如-Xss1024K。如果Stack太小,也會導致Stack溢漏。

nohup java -jar -Xmx4096m -Xms4096m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:PermSize=64MB -XX:MaxPermSize=256MB -Dcom.sun.management.jmxremote.port=2098 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.6.189 mms-service.jar >service.out 2>&1 &


nohup java -jar -Xmx4096m -Xms4096m -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:PermSize=64MB -XX:MaxPermSize=256MB -Dcom.sun.management.jmxremote.port=2099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.6.189 mms-service-api.jar >client.out 2>&1 &

jvm 調優