1. 程式人生 > >Java應用程式中的資源消耗分析

Java應用程式中的資源消耗分析

CPU的消耗

檢視CPU的消耗,目前最直接的辦法就是通過linux系統的top命令來檢視。如上文所述。

對Java應用程式而言,CPU的消耗主要體現在us,sy兩個值上,下面分析下這兩個值:

1. us

當us值過高時,表示執行的應用程式消耗了大部分的CPU。在這種情況下,對Java程式而言,如何找到具體消耗CPU的執行緒所執行的程式碼呢?

首先通過linux提供的命令找到消耗CPU嚴重的執行緒及其ID,將此ID轉換為16進位制的值。之後通過kill -3 <pid> 或 jastack的方式dump出應用的Java執行緒資訊。通過之前轉化的十六進位制的值找到對應nid值的執行緒,該執行緒就是消耗CPU的執行緒。

Java應用造成us高的主要原因是執行緒一直處於Runnable狀態,通常是這些執行緒在執行無阻塞,迴圈,正則或純粹的計算動作所照成的。另外一個可能會造成us高的原因是頻繁的GC。

2. sy

當sy的值高的時候,表示Linux話費了更多的時間在進行執行緒切換,Java應用造成這種現象的主要原因是啟動的執行緒比較多,而且這些執行緒多數處於不斷的阻塞(如鎖等待,IO等待)和執行狀態的變化中,這就導致了作業系統要不斷的切換執行的執行緒,產生了大量的上下文切換。

在這種情況下,對Java應用而言,最重要的就是要找出執行緒狀態不斷切換的原因。可以通過kill -3 <pid> 或 jastack的方式dump出應用的Java執行緒資訊,檢視執行緒的狀態資訊以及鎖資訊,找出等待狀態或鎖競爭過多的執行緒。

檔案IO的消耗

Linux系統在操作檔案是,將資料放入檔案緩衝區,直到記憶體不夠或者系統要釋放記憶體給使用者程序用。因此在檢視Linux記憶體狀況時(free -g)通常會發現可用的實體記憶體不多,但cached用了很多,這時Linux提升檔案IO速度的一種方法。這樣的做法下,如果物理空閒記憶體不夠,通常在Linux上只有寫檔案和第一次讀取檔案時會產生真正的檔案IO。

在Linux系統中,要跟蹤執行緒的檔案IO消耗,可以通過iostat命令來檢視:

Linux 3.10.0-327.el7.x86_64    04/20/2018      _x86_64_        (24 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          27.53    0.00    7.81    0.02    0.00   64.65

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
sda             112.64      2788.77     14334.25 53812230641 276594109712

在以上幾項指標中,其中Device表示裝置的卷標名或分割槽名。

  • tps是每秒的IO請求數,也是IO消耗情況中值得關注的數字。

在使用iostat檢視IO的消耗情況時,首先要關注的是CPU中的iowait%所佔的百分比,當iowait佔了主要的百分比時,就表示要關注IO方面的消耗了。這是可以通過iostat -x這樣的方式看箱子的檢視具體的情況。比如:

Linux 3.10.0-327.el7.x86_64      04/20/2018      _x86_64_        (24 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          27.53    0.00    7.81    0.02    0.00   64.65

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.01    50.21   23.72   88.92  2788.83 14334.30   304.02     0.05    0.47    0.16    0.55   0.13   1.51

其中值得關注的主要有:

  • r/s:表示每秒讀的請求數。
  • w/s:表示每秒寫的請求數。
  • await:表示每次IO操作的等待時間,單位為毫秒。
  • avgqu-sz:表示等待請求的佇列的平均長度。
  • svctm:表示平均每次裝置執行IO操作的時間。
  • util:表示一秒之中有百分之幾用於IO操作。

當檔案IO消耗過高時,對於Java應用最重要的是找到造成檔案IO消耗高的程式碼,比較簡單的尋找方法為採用pidstat命令(使用方式可使用命令man pidstat來檢視)來直接找到檔案IO操作多的執行緒。之後結合jstack找到對應的Java程式碼。

Java應用造成檔案IO消耗嚴重主要是多個執行緒需要進行大量的內容寫入(如日誌)的動作,或磁碟本身的處理速度比較慢,或檔案系統慢,或檔案本身已經很大。

記憶體消耗分析

堆記憶體分析可通過JVM提供的工具來做,這裡主要關注JVM堆以外的記憶體消耗(建立執行緒或使用Direct ByteBuffer等)。對於JVM對外記憶體的消耗,可以基於linux提供的命令來檢視:

1. vmstat

在Linux的console中輸入vmstat,輸出例子如下所示:‘

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 5  0      0 410076   1696 15959792    0    0   120   617    0    0 28  8 65  0  0

其中:

  • swpd:指虛擬記憶體中已經使用的部分,單位為kb。
  • free:表示空閒的實體記憶體。
  • buff:表示用於緩衝的記憶體。
  • cache:表示用於快取的記憶體。
  • si:每秒從disk讀至記憶體的資料量。
  • so:每秒從記憶體中寫入Disk的資料量。

如果swpd的值過高,通常是由於實體記憶體不夠用了,os將實體記憶體中的一部分資料轉為放入硬碟上進行儲存以便騰出足夠的空間給當前的應用程式使用。如swap IO發生的比較頻繁,那麼就會嚴重影響系統的效能。

由於Java應用是單程序的應用,因此只要JVM記憶體設定的不是過大,是不會操作到swap區域的。實體記憶體消耗過高可能是由於JVM記憶體設定過大,建立的Java執行緒過多或者通過Direct ByteBuffer往實體記憶體中防止了過多的物件造成的。