java性能優化常用工具jmap、jstack


jmap:java內存映像工具

jmap用於生成堆轉儲快照,比較常用的option包括-heap,-histo,-dump

[root@localhost script]# jmap -h
Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -clstats             to print class loader statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

java -heap [vmid],可以打印制定java虛擬機進程id的堆基本信息及使用情況,包括新生代大小、老年代大小、使用空間大小及比例。

jmap -histo [vmid],可以打印矩陣圖,可以看到java對象實例數量、大小、及具體是哪個對象。

jmap -dump:live,format=b,file=heap.bin <vmid>,可以生成一個heap.bin文件,從服務器上拿下來通過MAT工具進行分析,找到占用內存較高的對象,可以定位內存溢出的錯誤。

總體來說jmap命令操作簡單,但是用來優化系統和排查問題最常用的命令工具。

 

jstack:線程堆棧信息查看工具

jstack是jdk自帶的線程堆棧分析工具,使用該命令可以查看或導出 java 應用程序中線程堆棧信息。

[root@localhost script]# jstack -h
Usage:
    jstack [-l] <pid>
        (to connect to running process)
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process)
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file)
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server)

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message

jstack可以用來排查CPU使用高、內存使用高的情況,最終定位到問題點。

通過top命令查看CPU(內存)占用高的進程ID,然后通過命令top -H -p <vmid>,定位到具體的線程號pid。

拿到pid后,轉為16進制,通過命令printf "%x\n" pid,拿到hexpid

然后通過jstack <vmid> | grep <hexpid> -A 30,最終定位到問題點。

 

jstack的使用例子:

我首先創建了一個很簡單的springboot的項目,然后提供了一個接口,接口的方法體中有個雙層循環(方法代碼見下面的mockdump),把該項目部署到linux服務器上,啟動后的進程id是73165,執行shell命令top -H -p 73165,然后通過瀏覽器訪問該方法。之后觀察到線程號73183的cpu使用率高達95%(見下圖),然后通過jstack查看線程堆棧信息(見最后的shell命令執行的結果),發現異常信息指向了64行雙層循環處,即定位到了代碼異常點。

@GetMapping("/mockdump")
    public String mockdump(String loopcount) throws InterruptedException, ExecutionException {
        log.info("===mockdump===start===");
        for (int i = 0; i < Integer.parseInt(loopcount); i++) {
            ExecutorService executor =
                    new ThreadPoolExecutor(2, 10, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            Future<String> f = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    log.info("===wolaile===");
                    return "wolaile";
                }
            });
            long result = 0l;
            for (int m = 0; m < 999999; m++) {//這個是當前類的第64行
                for (int j = 0; j < 999999999; j++) {
                    result = result + j;
                }
            }
            calllist.add(f.get());
        }
        log.info("===mockdump===end===");
        return "success";
    }

[root@localhost ~]# printf "%x\n" 73183
11ddf
[root@localhost ~]# jstack 73165 | grep 11ddf -A 30
"http-nio-8080-exec-1" #13 daemon prio=5 os_prio=0 tid=0x00007fb33cdd6000 nid=0x11ddf runnable [0x00007fb30e6e7000]
   java.lang.Thread.State: RUNNABLE
        at com.demo.incubator.swaggerdemo.controller.SwaggerDemoController.mockdump(SwaggerDemoController.java:64)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1063)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:228)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:190)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:163)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM