发布新日志

  • 详细记录一下JAVA应用程序服务出现内存溢出的利用MAT分析过程

    2018-03-05 18:24:13

    说明:本次以系统在并发情况下后台出现java.lang.OutOfMemoryError:GC overhead limit exceeded错误来分析整个性能测试分析的一个过程。中间用到的工具包括了:loadrunner 、AWR 报告、jstack、MAT等

     

    1使用LoadRunner进行50用户的并发测试,先进行2分钟的预热测试,为了系统能用到缓存的地方都先进行缓存,然后进行5分钟的施压测试。

    2在施压5分钟的后半段时间,应用后台开始出现了“java.lang.OutOfMemoryError”的错误信息;

    具体错误信息如下:

     





    3、既然出现了OutOfMemoryError的错误信息,一般出现该错误信息都是堆内存的溢出,所以我们需要考虑捕捉一下堆内存的信息,捕捉堆内存的信息有2种方式:

    3.1 通过在应用中间件(weblogic、tomcat 等)上加入相应的JVM参数,具体参数如下(加入参数后,系统在出现OutOfMemoryError错误的时候便会自动生成类似java_pid9388.hprof的这样一个文件):

    -Xloggc:D:\heapdump\managed1_gc.log

    -XX:+HeapDumpOnOutOfMemoryError

    -XX:HeapDumpPath=D:\heapdump

    3.2 使用JDK自带的JMAP工具,具体使用方法如下:

    第一步:先使用jps.exe命令找到相应的java进程ID,一般找Server PID的;

    第二步:jmap.exe-dump:format=b,file=d:\dump\java_pid(第一步查询到的PID号).hprof PID(该地方一定要空格后跟着相应的PID号)

    如果只dump heap中的存活对象,则加上选项-live,如下:

    jmap.exe  -dump:live,format=b,file=/path/heap_pid. hprof 进程ID(PID)

    4.使用MAT工具来分析生成的hprof文件内容

    4.1打开需要分析的hprof文件:



    4.2 在Overview(概述)界面利用饼图的摘要信息来分析哪些对象比较占内存

     

    4.3分析Action部分内容:


    4.3.1点击“Leak Suspects”后的结果如下:

     

    4.3.2 在怀疑问题的第点Details



    4.3.3查看有问题的的类所引用的所有对象。此时使用鼠标左键点击,然后弹出菜单中进行如下选择:List Objects->with outgoing references

    (说明:

    图中的Shallow Heap(浅堆):指对象自身占用内存的大小,不包括它引用的对象。

    图中的 Retained Heap(深堆):指当前对象大小+当前对象可直接或间接引用到对象的大小总和

     


    此时可以点击鼠标左键,将sql语句的内容进行拷贝.



    此时就找到了问题。

  • 【JAVA 工具】jstack简单使用,定位死循环、线程阻塞、死锁等问题

    2018-03-05 17:24:48

    转载地址:http://www.cnblogs.com/chenpi/p/5377445.html


    当我们运行java程序时,发现程序不动,但又不知道是哪里出问题时,可以使用JDK自带的jstack工具去定位;

    废话不说,直接上例子吧,在window平台上的;

    死循环

    写个死循环的程序如下:

    复制代码
    package concurrency;
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            while (true) {
    
            }
        }
    }
    复制代码

    先运行以上程序,程序进入死循环;

    打开cmd,输入jps命令,jps很简单可以直接显示java进程的pid,如下为7588:

    或者输入tasklist,找到javaw.exe的PID,如下为7588:

    输入jstack 7588命令,找到跟我们自己代码相关的线程,如下为main线程,处于runnable状态,在main方法的第八行,也就是我们死循环的位置:

    Object.wait()情况

    写个小程序,调用wait使其中一线程等待,如下:

    复制代码
    package concurrency;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    class TestTask implements Runnable {
        @Override
        public void run() {
    
            synchronized (this) {
                try {
                    //等待被唤醒
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
    
            ExecutorService ex = Executors.newFixedThreadPool(1);
            ex.execute(new TestTask());
    
        }
    }
    复制代码

    同样我们先找到javaw.exe的PID,再利用jstack分析该PID,很快我们就找到了一个线程处于WAITING状态,在Test.java文件13行处,正是我们调用wait方法的地方,说明该线程目前还没等到notify,如下

    死锁

    写个简单的死锁例子,如下:

    复制代码
    package concurrency;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    class TestTask implements Runnable {
        private Object obj1;
        private Object obj2;
        private int order;
    
        public TestTask(int order, Object obj1, Object obj2) {
            this.order = order;
            this.obj1 = obj1;
            this.obj2 = obj2;
        }
    
        public void test1() throws InterruptedException {
            synchronized (obj1) {
                //建议线程调取器切换到其它线程运行
                Thread.yield();
                synchronized (obj2) {
                    System.out.println("test。。。");
                }
    
            }
        }
        public void test2() throws InterruptedException {
            synchronized (obj2) {
                Thread.yield();
                synchronized (obj1) {
                    System.out.println("test。。。");
                }
    
            }
        }
    
        @Override
        public void run() {
    
            while (true) {
                try {
                    if(this.order == 1){
                        this.test1();
                    }else{
                        this.test2();
                    }
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            Object obj1 = new Object();
            Object obj2 = new Object();
    
            ExecutorService ex = Executors.newFixedThreadPool(10);
            // 起10个线程
            for (int i = 0; i < 10; i++) {
                int rder = i%2==0 ? 1 : 0;
                ex.execute(new TestTask(order, obj1, obj2));
            }
    
        }
    }
    复制代码

    同样我们先找到javaw.exe的PID,再利用jstack分析该PID,很快jstack就帮我们找到了死锁的位置,如下所示:

    等待IO

    写个简单的等待用户输入例子:

    复制代码
    package concurrency;
    
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class Test {
    
        public static void main(String[] args) throws InterruptedException, IOException {
    
            InputStream is = System.in;
            int i = is.read();
            System.out.println("exit。");
    
        }
    }
    复制代码

    同样我们先找到javaw.exe的PID,再利用jstack分析该PID,很快jstack就帮我们找到了位置,Test.java文件12行,如下所示:

     

    其它

    像调用sleep使线程进入睡眠,suspend()暂停线程等就不举例了,都是类似的;

Open Toolbar