GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ IDE, Tools Hướng dẫn sử dụng VisualVM để đo hiệu năng chương trình Java

Hướng dẫn sử dụng VisualVM để đo hiệu năng chương trình Java

Đăng vào 25/10/2017 . Được đăng bởi GP Coder . 8862 Lượt xem . Toàn màn hình

Trong bài này, tôi sẽ hướng dẫn các bạn sử dụng VisualVM để đo hiệu năng của chương trình. Bài này tôi tham khảo từ bài viết của edwardthienhoang và có cập nhật lại một số chỗ theo phiên bản mới của VisualVM.

Hiệu năng chương trình bao gồm tốc độ xử lý, lượng RAM tiêu tốn và cả dung lượng ổ cứng tiêu tốn. Trong thời buổi mà ổ cứng lên tới cả Terabyte thì thì việc tiêu tốn vài GB để lưu trữ data cũng không được bận tâm nhiều lắm. Còn tốc độ xử lý và độ ngốn RAM thì luôn là bài toán với tất cả lập trình viên từ xưa đến nay.

Về thời gian thực thi, có một sốc cách để tính được thời gian thực thi của mỗi hàm, hay một vài đoạn code bằng cách gọi System.currentTimeMillis() ở đầu đoạn code và cuối đoạn code, qua đó tính được thời gian thực thi, hoặc cũng có thể dùng logging tool. Tuy nhiên những cách làm trên rất thủ công và tốn thời gian.

Còn về memory tiêu tốn, có thể mở Task Manager của Window lên để xem process của mình chạy hết bao nhiêu RAM hoặc dùng 1 số lớp có sẵn trong Java để trace ra heap dump tại từng thời điểm. Cũng giống như ở trên, việc làm này gây tốn thời gian và không chính xác nếu ta muốn xem trong chương trình hiện tại có bao nhiêu đối tượng A, B, C đang được khởi tạo, và tổng bộ nhớ dùng cho chúng là bao nhiêu thì không làm được.

Thay vì dùng những cách thủ công để đo hiệu năng của chương trình, chúng ta có thể nghĩ đến các công cụ giúp chúng ta làm chuyện đó. Chỉ cần lên mạng search vài từ khóa như: Java performance reasurement tool, Java performance analysis tool… sẽ cho các bạn rất nhiều lựa chọn.

Trong bài viết này, mình giới thiệu về VisualVM. Một công cụ hỗ trợ rất nhiều tính năng giúp cho việc đo lường hiệu năng của chương trình một cách dễ dàng và đặc biệt là nó cũng cung cấp cho các bạn một cái nhìn trực quan ví dụ như về các thread đang chạy hay thời gian thực thi của những hàm hiện tại… OK let’s start!

Nội dung

  • 1 Hướng dẫn cài đặt VisualVM
  • 2 Đo thời gian thực thi
  • 3 Đo độ tiêu thụ memory
  • 4 Chẩn đoán memory leak
  • 5 Theo dõi tất cả các thread đang chạy
  • 6 Kiểm tra deadlock trong chương trình

Hướng dẫn cài đặt VisualVM

Hướng dẫn download VisualVM

VisualVM có hai bản phân phối: VisualVM GitHub và Java VisualVM như một công cụ trong JDK. VisualVM tại GitHub là một phân phối với các tính năng mới nhất và sửa lỗi. Để có được một công cụ ổn định, sử dụng Java VisualVM có sẵn trong Oracle JDK của bạn.

Đầu tiên các bạn tải VisualVM bản mới nhất tại đây:

https://visualvm.github.io/download.html

Lưu ý là VisualVM chỉ chạy được trên JDK 6u7 trở lên, nếu máy các bạn chưa cài JDK hoặc JDK phiên bản cũ hơn thì có thể download bản mới nhất tại đây: http://www.oracle.com/technetwork/java/javase/downloads/index.html

Xem thêm bài viết hướng dẫn cài đặt JDK để biết cách cài đặt JDK.

Chạy VisualVM

Chạy VisualVM GitHub

Sau khi tải về, giải nén ra và mở file visualvm_137\bin\visualvm.exe lên sẽ có giao diện như thế này

Chạy VisualVM từ Oracle JDK

Mở thư mục chứa VisualVM trong thư mục cài đặt JDK. Ví dụ: C:\Program Files\Java\jdk1.8.0_131\bin

-> Chạy file jvisualvm.exe -> ta có giao diện như sau:

Ví dụ sử dụng VisualVM

Không chần chừ nữa, hãy tạo 1 project Java sau đó Run và xem ta có thể làm gì với VisualVM

public class OverviewTest {
 
    public static void main(String[] args) {
         
        System.out.println("Hello VisualVM");
         
        // Hey! I'm waiting you to switch to VisualVM
        // and see I'm also in there
        // just in 1 minute
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
}

Sau khi run đoạn code trên, các bạn chuyển qua VisualVM sẽ thấy được 1 process Java tương ứng với đoạn chương trình trên, double click vào sẽ thấy được thông tin overview về chương trình như: Process ID, main class, argument, phiên bản Java đang dùng…

Bây giờ hãy dùng VisualVM để kiểm tra các vấn đề về performance như:

  • Thời gian thực thi qua đó cải thiện tốc độ chương trình
  • Độ tiêu thụ memory cùng với việc kiểm tra và giải quyết memory leak
  • Theo dõi tất cả các thread đang chạy trong chương trình cùng với việc kiểm tra và giải quyết deadlock

Đo thời gian thực thi

public class PerformanceTest {
    
    public static void main(String[] args) {
 
        // Hey! I'm waiting you to switch to VisualVM
        // and see I'm also in there
        // just in 20 seconds
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        for (int i = 0; i < 10; i++) {
            PerformanceClass testClass = new PerformanceClass();
            testClass.foo1();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}
public class PerformanceClass {
     
    private int mField = 10;
     
    public void foo1() {
        mField += 3;
        System.out.println("foo1()");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        foo2();
        foo3();
    }
     
    public void foo2() {
        mField += 2;
        System.out.println("foo2()");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        foo3();
    }
     
    public void foo3() {
        mField += 1;
        System.out.println("foo3()");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Sau khi run chương trình, các bạn chuyển qua VisualVM, tìm process của chương trình đang chạy và double click vào, chọn thẻ Sampler và chọn button CPU, nó sẽ ra như sau:

Các bạn có thể nhìn thấy sự trực quan trên màn hình. Và có thể thấy thời gian thực thi của các hàm đang được gọi. Sau khi chương trình kết thúc ta có được kết quả cuối cùng

Dựa vào kết quả trên, có thể nhận biết được thời gian thực thi của mỗi hàm qua cột “Self Time“. Từ đó các bạn có thể kết luận rằng hàm nào đang tốn nhiều thời gian xử lý hơn bạn mong muốn, từ đó để đưa ra những điều chỉnh thích hợp để tăng tốc độ thực thi. Nếu muốn biết thời gian chiếm CPU thì có thể xem thêm ở cột “Self Time (CPU)“.

Đo độ tiêu thụ memory

Cùng đoạn code như trên, khi các bạn chạy chương trình Java và chuyển sang VisualVM, chọn thẻ Sampler và chọn button Memory, nó sẽ ra như sau:

Trong màn hình này, tại thẻ Heap Histogram, các bạn có thể xem trực quan số lượng class đã được load lên, tổng số instance của một lớp đang tồn tại trong bộ nhớ Heap và tổng số byte của chúng.

Chuyển qua thẻ Per thread allocations sẽ cho các bạn thấy các thông số giống ở trên nhưng cho từng thread.

Bên cạnh đó, chức năng Snapshot cho phép các bạn lưu / chụp lại các thông tin ngay tại thời điểm đó.

Bên cạnh chức năng theo dõi memory trực quan này, VisualVM còn hỗ trợ chúng ta xem HeapDump của chương trình đang chạy ngay thời điểm hiện tại. HeapDump sẽ cung cấp cho chúng ta nhiều thông tin hơn về giá trị của các biến thành viên trong instance đó. Để load HeadDump của chương trình hiện tại, các bạn click phải vào process của chương trình, sau đó chọn HeapDump, 1 tab HeapDump mới sẽ mở ra, các bạn chọn qua nút Classes. Tại đây các bạn có thể double click vào 1 class nào đó sẽ hiển ra danh sách các instance của lớp đó, double click vào 1 đối tượng, bạn sẽ thấy thông tin về các field của nó và danh sách các đối tượng khác đang reference đến nó.

Chẩn đoán memory leak

Bây giờ hãy chuyển sang 1 ví dụ để hiểu VisualVM cung cấp thông tin cho chúng ta như thế nào khi 1 chương trình bị leak memory

Các bạn có thể lên mạng để tìm hiểu 1 số nguyên nhân dẫn đến việc leak memory trong Java, ở đây mình lấy 1 ví dụ về hàm subString. Từ Java 6 trở về trước, khi gọi hàm subString trên 1 đối tượng String luôn luôn gây ra memory leak, và lỗi này đã được fix từ Java 7.

import java.util.ArrayList;
  import java.util.List;
   
  class Stringer {
      static final int MB = 1024 * 1024;
   
      static String createLongString(int length) {
          StringBuilder sb = new StringBuilder(length);
          for (int i = 0; i < length; i++)
              sb.append('a');
          sb.append(System.nanoTime());
          return sb.toString();
      }
   
      public static void main(String[] args) {
          try {
              Thread.sleep(10000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
           
          List<String> substrings = new ArrayList<String>();
          for (int i = 0; i < 100; i++) {
              String longStr = createLongString(MB);
              // Leak here. Just in Java 6-
              String subStr = longStr.substring(1, 10);
              substrings.add(subStr);
          }
           
          try {
              Thread.sleep(20000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }

Để tái hiện memory leak, mình sẽ chạy chương trình trên Java 6, kết quả đoạn code trên sẽ như sau:

Còn khi chạy trên Java 8, thì kết quả như sau:

Qua 2 ví dụ trên, cho ta thấy nếu memory tiêu tốn trong chương trình tăng một cách bất thường hoặc biến thiên tăng dần theo thời gian, thì có thể chương trình bạn đang gặp vấn đề memory leak.

Theo dõi tất cả các thread đang chạy

VisualVM cũng hỗ trợ việc theo dõi một cách trực quan về vòng đời của tất cả các thread đang chạy trong chương trình. Với mỗi trạng thái của Thread sẽ được biểu diễn bằng một màu khác nhau, bạn sẽ dễ dàng hình dung ra mọi thứ đang hoạt động thế nào. Bắt đầu với 1 đoạn code test.

public class ThreadTest {
 
    public static void main(String[] args) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
 
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out
                                .println(i + Thread.currentThread().getName());
                        try {
                            URL url = new URL(
                                    "http://www.google.com/");
                            URLConnection conn = url.openConnection();
                            conn.connect();
                            System.out.println("Dude its Working Fine ! ");
                        } catch (Exception e) {
                            System.out.println("Not Working");
                        }
                    }
 
                }
            }).start();
        }
    }
 
}

Chuyển sang VisualVM, double click vào ProcessID, sau đó chọn thẻ Thread Dump sẽ thấy được các thread đang chạy trong chương trình.

Ứng với mỗi trạng thái của thread được biểu diễn bằng các màu khác nhau.

  • Màu xanh lá: Thread đang chạy
  • Màu tím: Thread đang sleep khi gọi câu lệnh Thread.sleep(…)
  • Màu vàng: Thread đang wait khi gọi hàm wait()
  • Màu đỏ: Thread đang bị block và đợi đến lượt để vào khối lệnh synchronize.

Kiểm tra deadlock trong chương trình

Giả sử như có rất nhiều thread đang bị block để đợi đến lượt vào khối lệnh synchronize mà thread hiện tại đang giữ. Tuy nhiên vì một lý do nào đó mà thread hiện tại không thể thoát ra khối lệnh synchronize để nhường chỗ cho thread khác dẫn đến các thread khác sẽ phải chờ rất lâu thậm chí là mãi mãi dẫn đến việc “tắc đường” làm ngưng trệ toàn bộ chương trình. Vì vậy trong môi trường multithread, khi thấy chương trình mình bị “đơ” thì rất có thể đã xảy ra deadlock. Và để chắc chắn hơn, các bạn có thể dùng VisualVM để kiểm tra có deadlock hay không. Ví dụ như đoạn code dưới đây, cùng kiểm tra xem hình hài của deadlock như thế nào.

public class DeadlockTest {
 
    public static void main(String[] args) throws InterruptedException {
        Object obj1 = new Object();
        Object obj2 = new Object();
        Object obj3 = new Object();
      
        Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
        Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
        Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");
          
        t1.start();
        Thread.sleep(5000);
        t2.start();
        Thread.sleep(5000);
        t3.start();
          
    }
 
}
class SyncThread implements Runnable{
    private Object obj1;
    private Object obj2;
  
    public SyncThread(Object o1, Object o2){
        this.obj1=o1;
        this.obj2=o2;
    }
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on "+obj1);
        synchronized (obj1) {
         System.out.println(name + " acquired lock on "+obj1);
         work();
         System.out.println(name + " acquiring lock on "+obj2);
         synchronized (obj2) {
            System.out.println(name + " acquired lock on "+obj2);
            work();
        }
         System.out.println(name + " released lock on "+obj2);
        }
        System.out.println(name + " released lock on "+obj1);
        System.out.println(name + " finished execution.");
    }
    private void work() {
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Khi chạy VisualVM sẽ thấy như sau:

3 thread t1, t2, t3 hiện đang ở trạng thái màu đỏ, nghĩa là cả 3 cùng đang chờ lẫn nhau dẫn đến không thread nào có thể chạy tiếp được dẫn đến deadlock.

Kết thúc bài viết hi vọng đã chia sẻ cho các bạn một công cụ hữu ích trong việc theo dõi và đo hiệu năng chương trình Java.

Nguồn: https://edwardthienhoang.wordpress.com

5.0
09
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: IDE, Tools Được gắn thẻ: JDK, Performance

Hướng dẫn sử dụng plugin Vanaraha để kiểm tra code trùng lặp
Autoboxing và Unboxing trong Java

Có thể bạn muốn xem:

  • Các plugin Eclipse thường dùng (22/10/2017)
  • Hướng dẫn sử dụng JAutodoc để sinh comment trong Eclipse (22/10/2017)
  • Hướng dẫn cài đặt JDK (18/10/2017)
  • Hướng dẫn sử dụng plugin JadClipse để xem nội dung file .class (05/11/2017)
  • Hướng dẫn sử dụng plugin Vanaraha để kiểm tra code trùng lặp (24/10/2017)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (97349 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (96986 lượt xem)
  • Giới thiệu Design Patterns (86646 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (85481 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83023 lượt xem)

Nội dung bài viết

  • 1 Hướng dẫn cài đặt VisualVM
  • 2 Đo thời gian thực thi
  • 3 Đo độ tiêu thụ memory
  • 4 Chẩn đoán memory leak
  • 5 Theo dõi tất cả các thread đang chạy
  • 6 Kiểm tra deadlock trong chương trình

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link