JVM系列之常用命令(中)

JVM系列之常用命令(中),第1张

前言

上一篇介绍了一些简单的java命令,这次难度稍微加深一点点,介绍几个高级一点的java命令jinfo和jstack。

jinfo

也许当你运行jinfo命令时,可能会遇到java.lang.InternalError:Metadata does not appear to be polymorphic的异常,如下图:

解决方式就是安装相应的jdk debug包。因为我的环境是Ubuntu和openjdk1.8的环境,所以执行以下命令,安装openjdk-8-dbg包

sudo apt-get install openjdk-8-dbg
–flags查看jvm参数

执行jinfo -flags命令可以查看java进程的相关参数,包括jvm默认缺省的参数也显示出来了,这个是jps -lv是查看不到的。

jinfo -flags pid

-sysprops查看系统参数

执行jinfo -sysprops 命令可以查看系统相关参数

jinfo -sysprops 312

动态修改jvm参数

先查看那些jvm参数可以修改

java -XX:PrintFlagsFinal -version | grep manageable


能调整的jvm参数并不多,比较实用就PrintGC,下面试试吧

jinfo -flag PrintGC=1 312

运行后可以看到参数确实被修改了

jstack

jstack命令是非常实用的一个命令,可以查看死锁和一些线程特别高的问题。

死锁

先写一个死锁的java程序

public class DeadThread {
    static final Object lockOne = new Object();
    static final Object lockTwo = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(new ThreadOne());
        Thread thread2 = new Thread(new ThreadTwo());
        thread1.start();
        thread2.start();
    }

    static class ThreadOne implements Runnable {

        @Override
        public void run() {
            try {
                synchronized (lockOne) {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(3000);
                    synchronized (lockTwo) {
                        System.out.println(Thread.currentThread().getName());
                        Thread.sleep(3000);
                    }
                }
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class ThreadTwo implements Runnable {

        @Override
        public void run() {
            try {
                synchronized (lockTwo) {
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(3000);
                    synchronized (lockOne) {
                        System.out.println(Thread.currentThread().getName());
                        Thread.sleep(3000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

然后编译java文件,并运行起来

javac DeadThread.java
java DeadThread

使用jstack输出线程运行日志,可以方便技术人员去查找有不有死锁

jstack -l pid > jstack.log

打开jstack.log日志文件,部分日志如下

输出的文件最下方有死锁的提示Found 1 deadlock,具体死锁的线程就是刚才那个java代码中,ThreadOne和ThreadTwo相互锁了对方的对象,造成了死锁。日志文件也可以看得出来locked和waiting。Thread-0锁了0x000000077db71098,等待0x000000077db710a8,而另一个Thread-1正好相反,所以造成了死锁。
这里需要注意的是,开发人员在写代码的时候,建议给自己的线程起一个名字,方便后期出问题时可以快速定位。一个真实的项目里面,线程是很多的,有些问题很难排查,有了线程名字就减少了一定的工作量。

CPU或内存占用率很高

有些公司可以上生产环境的排查问题,有时发现CPU很高时

测试用的java代码如下

public class HighCPU {
    static final Object lockOne = new Object();
    static final Object lockTwo = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            while (true){
                int num = 100;
                for (int i = 0; i < 1000; i++) {
                    num /= 10;
                    num *= 10;
                }
            }
        }).start();
    }
}

使用top -Hp pid,查看线程。可以查看具体是哪个线程出现问题,如下图

top -Hp 646


很显然有一个名称为Thread-0的线程CPU很高,于是我们也可以使用jstack看一下线程日志。
这里的pid指的是线程id,pid666的线程需要转成16进制,因为jstack日志里面的id是16进制的。可以使用printf命令转换为16进制,也用一些百度里或者手机里的工具,很简单。

printf "%x\n" 666


线程id666转换成16进制就是29a。在jstack日志文件搜索0x29a,可以找到一个nid=0x29a的线程,这样排查就可以定位大概是那一行代码的问题了。从下图,可以发现是HighCPU这个类的第9行正在运行,问题估计就在那里了。

回到代码,来到第9行,可以发现这里有一个死循环在不停的运行。

总结

这一篇介绍两个高级一点的java命令,也是排查程序很实用的命令。当然很多大公司是不能上生产环境服务器的,也就不可能在生产环境使用上面的命令。不过测试环境还是可以开放给开发人员上去调试的。那如果生产环境不能上,又出现了测试环境无法重现的问题怎么办呢?我们还有dump文件,时候不早了,下一篇再介绍吧。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/883664.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-14
下一篇 2022-05-14

发表评论

登录后才能评论

评论列表(0条)

保存