java:線上問題排查常用手段

來源:互聯網
上載者:User

標籤:countdown   cache   top   visual   啟動參數   ora   java oom   cup   java7   

一、jmap找出佔用記憶體較大的執行個體

先給個範例程式碼:

import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;/** * Created by 菩提樹下的楊過 on 05/09/2017. */public class OOMTest {    public static void main(String[] args) throws InterruptedException {        CountDownLatch latch = new CountDownLatch(1);        int max = 10000;        List<Person> list = new ArrayList<>(max);        for (int j = 0; j < max; j++) {            Person p = new Person();            p.setAge(100);            p.setName("菩提樹下的楊過");            list.add(p);        }        System.out.println("ready!");        latch.await();    }    public static class Person {        private String name;        private int age;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getAge() {            return age;        }        public void setAge(int age) {            this.age = age;        }    }}  

List中放了1w個Person對象的執行個體,先把這段程式跑起來

javac OOMTest.java

java OOMTest

然後再開一個視窗,jps -l  找出該程式的pid

然後執行 jmap -histo:live 7320 

輸出結果,會按記憶體使用量量,從大到小依次把對象的實際個數,佔用記憶體數量(位元組數)列印出來,最後還會輸出匯總資訊

以上面的樣本來說,OOMTest$Person這個類的執行個體數為10000個,總共佔用240000位元組(註:即每個執行個體24位元組),這個程式總佔用記憶體數為725464位元組,約:0.69M。

另外還有一些[C,[B這類class name,大概意思為:

[C is a char[]
[S is a short[]
[I is a int[]
[B is a byte[]
[[I is a int[][]

[C對象往往跟String有關,String其內部使用final char[]數組來儲存資料的

constMethodKlass/ methodKlass/ constantPoolKlass/ constantPoolCacheKlass/ instanceKlassKlass/ methodDataKlass

與Classloader相關,常駐與Perm區。

 

二、找出某個java應用開啟的控制代碼數及線程數

ll /proc/{pid}/fd | wc -l 查看開啟的控制代碼數

ll /proc/{pid}task | wc -l 查看線程數

 

三、jmap 查看堆記憶體的各項配置

jmap -heap pid 可以看到類似下面的輸出:

using thread-local object allocation.Parallel GC with 4 thread(s)  //當前使用的GC方式(並行GC)Heap Configuration:  //堆記憶體配置   MinHeapFreeRatio         = 0   //對應jvm啟動參數-XX:MinHeapFreeRatio設定JVM堆最小空閑比率(java8預設0)   MaxHeapFreeRatio         = 100 //對應jvm啟動參數-XX:MaxHeapFreeRatio設定JVM堆最大空閑比率    MaxHeapSize              = 8388608 (8.0MB) //對應jvm啟動參數-XX:MaxHeapSize=設定JVM堆的最大大小(或-Xmx參數)   NewSize                  = 5242880 (5.0MB) //對應jvm啟動參數-XX:NewSize=設定JVM堆的‘新生代’的預設   MaxNewSize               = 5242880 (5.0MB) //對應jvm啟動參數-XX:MaxNewSize=設定JVM堆的‘新生代’的最大大小    OldSize                  = 3145728 (3.0MB) //對應jvm啟動參數-XX:OldSize=設定JVM堆的‘老生代’的大小    NewRatio                 = 2 //對應jvm啟動參數-XX:NewRatio=:‘新生代’和‘老生代’的大小比率    SurvivorRatio            = 8 //對應jvm啟動參數-XX:SurvivorRatio=設定年輕代中Eden區與Survivor區的大小比值    MetaspaceSize            = 21807104 (20.796875MB)   CompressedClassSpaceSize = 1073741824 (1024.0MB)   MaxMetaspaceSize         = 17592186044415 MB   G1HeapRegionSize         = 0 (0.0MB)Heap Usage: //堆記憶體使用量情況 PS Young GenerationEden Space: //Eden區分布   capacity = 2621440 (2.5MB) //Eden區總容量   used     = 2328088 (2.2202377319335938MB) //Eden區已使用   free     = 293352 (0.27976226806640625MB) //Eden區剩餘容量   88.80950927734375% usedFrom Space: //其中一個Survivor區的記憶體分布   capacity = 1572864 (1.5MB)   used     = 360448 (0.34375MB)   free     = 1212416 (1.15625MB)   22.916666666666668% usedTo Space: //另一個Survivor區的記憶體分布   capacity = 1048576 (1.0MB)   used     = 0 (0.0MB)   free     = 1048576 (1.0MB)   0.0% usedPS Old Generation //當前的Old區記憶體分布    capacity = 3145728 (3.0MB)   used     = 1458968 (1.3913803100585938MB)   free     = 1686760 (1.6086196899414062MB)   46.37934366861979% used3759 interned Strings occupying 298824 bytes.

註:5-16行是堆記憶體的主要配置,這些參數都可以通過 java -XX:參數名=參數值 來調整其大小,比如:

java -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=80 -Xmx100m -XX:MetaspaceSize=50M -XX:NewRatio=3 將影響MinHeapFreeRatio、MaxHeapFreeRatio、MaxHeapSize、MetaspaceSize、NewRatio的值

注意下NewRatio,這個值指的 老年代(Old Generation): 新生代(Young Generation)的比值,上面設定成3,所以OldSize為75m,而NewSize為25m,參考:

注:這是jdk7的,jdk8中Permanent Generation被去掉了,新加入了Metaspace區,但這個區別不影響對 新生代、老生代的理解。

新生代(Young Generation)又可以細分為eden、s0、s1 三大塊。

java7與java8的記憶體變化,大致如。

SurvirorRatio這個要難算一點,按Oracle官網的解釋:https://docs.oracle.com/cd/E19159-01/819-3681/abeil/index.html ,預設值是8,即:每塊survivor: eden區的大小為1:8,換句話說 s0 = s1 = 1 / (1+1+8) = 1/10

註:雖然官網這麼解釋,但是我實際算了下,好象並不是嚴格按這個比例來算的,只能大概說是這個分配比例。(結論就是:SurvirorRatio設定越大,eden區就越大)

 

四、找出佔用CPU最高的線程

先來一段示範代碼:

import java.util.concurrent.CountDownLatch;/** * Created by 菩提樹下的楊過 on 05/09/2017. */public class OOMTest {    public static void main(String[] args) throws InterruptedException {        CountDownLatch latch = new CountDownLatch(1);        int max = 100;        for (int i = 0; i < max; i++) {            Thread t = new Thread() {                public void run() {                    try {                        Thread.sleep(50);                    } catch (InterruptedException e) {                        Thread.currentThread().interrupt();                    }                }            };            t.setName("thread-" + i);            t.start();        }        Thread t = new Thread() {            public void run() {                int i = 0;                while (true) {                    i = (i++) / 10;                }            }        };        t.setName("BUSY THREAD");        t.start();        System.out.println("ready");        latch.await();    }}

這裡面有100個線程是空轉的,另外還有一個線程BUSY THREAD在狂跑CPU。

javac OOMTest.java

java OOMTest

把程式跑起來,jps -l 找出pid,然後  top -Hp pid

可以看到pid 16813這個對應的線程,把CPU快跑滿了,達到了98.5%

接下來,將16813轉換成16進位 ,即41ad ,然後

jstack pid | grep ‘41ad‘

我們就把最忙的這個線程BUSY THREAD給找出來了(註:這個技巧再次說明了,給線程取個好名字相當重要!)

tips:如果使用spring-boot的話,直接在瀏覽器裡查看/dump端點,也可以達到類似jstack的效果。

 

五、jvisualvm 查看運行情況

JDK_HOME/bin下有一個內建的jvisualvm工具,可以圖形化的查看GC情況(註:要安裝外掛程式)

java.net這個網站已經被oracle關了,所以安裝外掛程式這裡,有點小麻煩,先到https://visualvm.github.io/pluginscenters.html 這裡找到jvisualvm對應的jdk版本號碼,以jdk8為例,地址就是 https://visualvm.github.io/uc/8u131/updates.xml.gz

然後,把這個地址在Plugins裡的Settings裡改一下,然後Available Plugin這裡,就能看到可用外掛程式了,選擇GC外掛程式並安裝。

可以來一段代碼,然後用jvisualvm來看下GC情況

import java.util.ArrayList;import java.util.List;/** * Created by 菩提樹下的楊過 on 05/09/2017. */public class OOMTest {    public static void main(String[] args) throws InterruptedException {        List<String> list = new ArrayList<>();        while (true) {            Thread.sleep(10);            list.add("菩提樹下的楊過" + System.currentTimeMillis());        }    }}

 

可以直觀的看到Old區,Eden區,S0,S1以及Metaspace區的記憶體變化情況。

此外,還可以看到佔用記憶體最多的類(即:本文最開始提到的)

也可以查看哪些線程最忙

 

參考文章:

Java GC系列 http://www.importnew.com/13504.html

深入理解 Java G1 垃圾收集器 http://blog.jobbole.com/109170/

java:線上問題排查常用手段

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.