1. 程式人生 > >Java虛擬機性能監控與調優實戰

Java虛擬機性能監控與調優實戰

垃圾 don 監控 java虛擬機規範 異常 路徑 AS 允許 即時編譯器

Java虛擬機的內存結構,區別於側重於多線程的Java內存模型(Java Memory Model)

  

  但在此之前,我們該思考一下:JVM的內存結構為什麽要這樣劃分?

  我認為主要是依據於不同數據的更新頻率、訪問速度要求、垃圾收集管理由此劃分的JVM的五大內存區-- PC寄存器、JVM Stack 、Native Method Stack 、Java Heap、方法區、共享區

  下面主要詳細解釋一下這五個內存區:

  1.PC寄存器

  PC寄存器(Program Counter Register,程序計數器),是一塊較小的內存空間,可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

  由於JVM的多線程是通過輪流切換分配CPU執行時間的方式來實現的,在某個特定時刻,一個CPU/內核只會執行一條線程的指令。為了線程切換後能恢復到正確的執行位置,每條線程都需要有一個私有、獨立的PC寄存器,各線程間互不影響,我們稱這類內存區域為“線程私有”的內存。
  如果線程當前執行的是一個Java方法,PC寄存器記錄的是正在執行的虛擬機字節碼指令的地址
  如果線程正在執行的是native方法,它的值則為空(undefined)

  2.JVM Stack

  和PC寄存器一樣,JVM Stack也是線程私有的。它的生命周期與線程相同。JVM Stack描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個Frame用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用到執行完成的過程,就對應著一個Frame在JVM Stack中入棧到出棧的過程

  經常有人把Java內存分為堆內存(Heap)和棧內存(Stack),這種分法比較粗糙,Java內存區域的劃分遠比這復雜。其中所指“棧”就是現在講的JVM Stack,或者說是JVM Stack中局部變量表部分(直接越過了Stack和Frame)。

  當線程請求分配的棧容量超過JVM允許的最大容量時,將拋出StackOverflowError;

  如果JVM可動態擴展,並且擴展的動作已經嘗試過,但是目前無法申請到足夠的內存去完成擴展,或在建立新的線程時沒有足夠的內存去創建對應的JVM Stack,將拋出OutOfMemoryError。
  StackOverflowError 表示 請求 > Stack.Max
  OutOfMemoryError 表示 請求 > 可分配內存

  3.本地方法棧

  本地方法棧與JVM Stack所發揮的作用是非常相似的,區別只是JVM Stack為虛擬機執行Java方法服務,而本地方法棧是為虛擬機使用到的Native方法服務。

  在虛擬機規範中對本地方法棧中方法使用的語言、使用方式與數據結構沒有強制規定,由實現自由選擇。甚至有的虛擬機(如HotSpot)直接將本地方法棧和JVM Stack合二為一。與JVM Stack一樣,也會拋出StackOverflowError和OutOfMemoryError異常

  def default_key_func(key, key_prefix, version):
  
  """
  
  Default function to generate keys.
  
  Constructs the key used by all other methods. By default it prepends
  
  the `key_prefix‘. KEY_www.taohuaqing178.com FUNCTION can be used to specify an alternate
  
  function with custom key making behavior.
  
  """
  
  return ‘%s:%s:%s‘ % (key_prefix,www.dongfan178.com/ version, key)
  
  def get_key_func(key_func):
  
  """
  
  Function to decide which key function to use.
  
  Defaults to ``default_key_func``.
  
  """
  
  if key_func is not None:
  
  if callable(key_func):
  
  return key_func
  
  else:
  
  return import_string(www.fencaiyule.cn/ key_func)
  
  return default_key_func
  
  復制代碼
  
  內存:
  
  說明:內存版本的時候,必須設置一個值,這個值是唯一的,此緩存將內容保存至內存的變量中
  
  
  CACHES = {
  
  ‘default‘: {
  
  ‘BACKEND‘: ‘django.core.cache.backends.locmem.LocMemCache‘,
  
  ‘LOCATION‘: ‘www.chaoyueyule.com unique-snowflake‘, #這邊必須設置一個值,這個值是唯一的
  
  }
  
  #其他的配置和開發調試版本一樣
  
  }
  
  文件:說明:此緩存將內容保存至文件

  CACHES = {
  
  ‘default‘: {
  
  ‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘,
  
  ‘LOCATION‘: ‘/var/tmp/django_cache‘, #緩存存放的路徑
  
  }
  
  #其他的配置和開發調試版本一樣
  
  }
  

  4.Java Heap

  對大多數應用來說,Java堆(Java Heap)是JVM所管理的內存中最大的一塊,它是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。該區的唯一目的就是存放對象實例,幾乎所有的對象實例都在這裏分配內存。

  Java Heap是GC管理的主要區域。由於現在收集器基本都采用了分代收集算法,所以Java Heap還細分為:新生代和老年代;再細一點的 Eden空間、From Survivor空間、To Survivor空間等。不過無論怎麽劃分,都與存放內容無關,無論哪個區域存儲的都是對象實例。

  在實現時,既可以是固定大小的,也可以是擴展的,不過主流的虛擬機都是按照可擴展來實現的(-Xmx和-Xms)。如果堆 中沒有內存完成實例分配,並且堆無法再擴展時,就會拋出OutOfMemoryError

  5.方法區

  與Java Heap一樣,方法區是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。雖然JVM規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫Non-Heap(非堆),目的應該是與Java Heap分區來的。

  Java虛擬機規範對方法區的限制非常寬松,可以選擇不實現垃圾收集。垃圾收集行為在本區是比較少出現的,但非數據進入方法區就如永久代的名字一樣“永久”存在了。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載,一般來說,這個區域的回收“成績”很難令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域的回收確實是有必要的。

  

  而運行時常量池作為方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載後進入方法區的運行時常量池中存放。

  Java虛擬機對Class文件每一部分(自然也包括常量池)的格式都有嚴格規定,每一個字節用於存儲哪種數據都必須符合規範上的要求才會被虛擬機認可、裝載和執行,但對於運行時常量池,Java虛擬機規範沒有做任何細節的要求,不同的提供商實現的虛擬機可以按照自己的需要來實現這個內存區域。

  既然運行時常量池是方法區的一部分,自然受到方法區內存的限制,當常量池無法再申請到內存時拋出OutOfMemoryError異常。

  大概就是這樣了,拜!

Java虛擬機性能監控與調優實戰