认识JVM性能监控与故障处理工具&深入理解Java内存模型

 2019-12-10 16:21  阅读(1428)
文章分类:Java Core

offer 105

先来复习:

  1. 内存区,jvm的内存区,java语言的内存调试工具,jdk bin目录下的工具。

以下从《深入理解Java虚拟机》获取

名称 主要作用
名称 主要作用
jps JVMProcessStatusTool,显示指定系统内所有的HotSpot虚拟机进程
jstat JVMStatisticsMonitoringTool,用于收集HotSpot虚拟机各方面的运行数据
jinfo ConfigurationInfoforJava,显示虚拟机配置信息
Jmap MemoryMapforJava,生成虚拟机的内存转储快照(heapdump文件)
jhat JVMHeapDumpBrowser,用于分析heapdump文件,它会建立一个HTTP/HTML服务器,让用户在浏览器上看到分析结果。
jstack StackTraceforJava,显示虚拟机的线程快照

jps命令格式: jps [ options ] [ hostid ] jps可以通过RMI协议查询开启了RMI服务的远程虚拟机进程状态,hostid为RMI注册表中注册的主机名。 jps工具主要选项 (1)-q : 只输出LVMID,省略主类的名称 (2)-m :输出虚拟机进程启动时传递给主类main()函数的参数 (3)-l :输出主类的全名,如果进程执行的是Jar包,输出Jar路径 (4)-v:输出虚拟机进程启动时JVM参数 执行结果如下:

20191210001758\_1.png

运行代码:

import java.util.*;
    public class template{
        public static void main(String[] args){
            test();
            if(args[0]==null){
                System.out.println("it is null");
            }
            else{
                System.out.println(args[0]);
            }

            while(true){
                new temp();
            }
        }

        static void test()
        {
            String a = "hEllo,world";

            int c = a.codePointCount(1,6);

            char[] b = a.toCharArray();

            byte[] d = a.getBytes();
            System.out.println(Arrays.toString(d));
            System.out.println(c);

        }

    }

    class temp{
        temp(){System.out.println("hello, I am temp");}
    }

在控制台输入:java template kingpin : 结果自然是一只输出:

20191210001758\_2.png

然后在运行 jps -q -m -l :

20191210001758\_3.png

  1. jstat:虚拟机统计信息监视工具

然后让上述代码继续运行,用jstat查看结果:

jstat -gc 5096 250 5

20191210001758\_4.png

分析一下:(单位为KB)

S0C:Survivor0 的容量, capacity

S1C:Survivor1, 与S0C一样

S0U: Survivor0 的利用情况,utilization

S1U:

EC:eden 新生代使用情况

EU:

OC,OU:老年代的一些情况

PC,PU:Permanent space 当前永久代的情况

YGC: Numbers of young generation GC events

YGCT: Young generation Garbage collection time

FGC : full gc 次数

FGCT: full gc 时间

GCT: total gc 时间

由上图可以看出其实这个程序运行了这么久一直增加的只有EU , 在笔者写博文的时候程序还是一直在运行,现在再查看一下情况:

20191210001758\_5.png

这个例子就比较好点,因为没1000 毫秒才查询一次gc的情况:

在最后的此一次查询中 65 次 gc 执行了, 并且使用了 0.117 秒,个人估计的话这个时间是累积的。

请参见博文:jstat全解

PS: 补充Young generation 中 Eden代和 Survivor 代的主要区别:

大多数情况下,对象在新生代 Eden区中分配,当Eden区没有足够的空间进行分配时,虚拟机将发起一次minor gc, 就是新生代的 垃圾收集

-XX:SurvivorRatio =8 决定了YG中Eden分区与一个Survivor 去的空间比例是 8:1,

对象的运行原则是 Eden 与 Survivor ,先放Eden ,再放Survivor 如果 Eden中没有空间,发起一次发起一次 Minor Gc , 还是没有空间,放入 Survivor , 还是没有(比如说大对象),分配担保机制提前转移到老年代。

如果对象在Eden出生并经过第一次Minor gc后仍然存活,并且能被Survivor 容纳的话,将被移动到Survivor空间中,并将对象年龄设为1, 在Survivor中每熬过一次 Minor gc, 年龄每增加一岁,默认增加到 15岁的时候会升到老年代。

下面先试一试 -XX:SurvivorRatio=7(注意无空格) 的运行:

20191210001758\_6.png

可以看到:

20191210001758\_7.png

这个时候 EC 已经是 S0C 和 S1C的7倍。

  1. jinfo:Java配置信息工具

20191210001758\_8.png

  1. Jmap:Java内存影像工具

又是在windows平台下受限制的一个工具

20191210001758\_9.png

-histo 显示堆中对象统计信息,包括类,实例数量和合计容量等。

  1. jhat:虚拟机堆转储快照分析工具

分析功能相对简陋,有更专业的例如:专业用于分析dump文件的Eclipse Memory Analyzer, IBM HeapAnalyzer等

不予展示

  1. jstack : Java 堆栈跟踪工具

20191210001758\_10.png

选项 作用
选项 作用
-F 当正常输出的请求不被响应时,强制输出线程堆栈
-l 除堆栈外,显示关于锁的附加信息
-m 如果调用奥本地方法的话,可以显示C/C++的堆栈

可视化工具:

  1. JConsole:

在bin目录下 启动 JConsole.exe

20191210001758\_11.png

很多功能都还没时间看

  1. VisualVM:多合一故障处理工具

20191210001758\_12.png

还需要安装很多的插件VisualVM的功能才能完善。

重看 《深入理解Java虚拟机》一书,内容多得惊人,以前的学习是多么水。

  1. 深入理解Java内存模型的全面复习和总结与回顾:

深入理解Java内存模型(程晓明),程晓明就职于富士通南大,日本第一次IT企业。

深入理解Java内存模型(一)基础

并发编程中两大问题:线程之间如何通信及线程之间如何同步。

通信是指线程之间以何种机制来交换信息。命令式编程中,通信机制有两种:共享内存,消息传递。

同步是指程序控制不同线程之间操作发生相对顺序的机制,在共享内存并发模型,同步显示进行。消息传递的并发模型中,同步是隐式的,应为消息的发送在接受之前。

Java的并发采用的是共享内存模型。这就要求程序员了解隐式进行的线程之间通信的工作机制。

实例域,静态域,数组元素存储在堆内存中,堆内存在线程之间共享。

局部变量,方法定义参数,异常处理器参数,不会再内存之间共享,不会有内存可见性问题。

模拟一个线程本地内存(本地内存是JMM的一个抽象概念,并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化)

线程A和线程B之间要通信:

  1. 线程A把本地内存A中更新过的共享变量刷新到主内存。
  2. 然后,线程B到主内存中去读取线程A已经更新过的共享变量。

重排序:

1.编译器,编译器在不改变但线程程序语义的前提下,重新安排语句的执行。

2.指令级并行,不存在数据依赖,处理器可以改变语句对应机器指令的执行顺序。

3.内存系统,缓存和读/写缓冲区。

20191210001758\_13.png

对于编译器重排序(1),JMM禁止特定类型的重排。

对于处理器重排(2和3),JMM要求java编译器在生成指令序列时,插入特定类型的内存屏障(memory barriers,intel : memory fence)

JMM属于语言级的内存模型。

由于写缓冲区仅对自己的处理器可见。现代处理器都会使用写缓冲区,因此现代的处理器都会允许对写读操作重排。

20191210001758\_14.png

内存屏障:

20191210001758\_15.png

StoreLoad Barriers 是一个“全能型”的屏障,它同时具有其他三个屏障的效果,执行该屏障开销会很昂贵,因为当前处理器要把写缓冲区中的数据全部刷新到内存中。

JSP-133内存模型,happens-before

20191210001758\_16.png

深入理解Java内存模型(二):重排序

请注意:不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑

as-if-serial语义:

不管怎么重排序,(单线程)程序的执行结果不能被改变。(但是确实允许重排序)

在单线程程序中,对控制依赖的重排序是不会改变执行结果(同时也允许),as-if-serial语义也允许,但是在多线程中,对控制依赖的操作重排序,可能会改变程序执行结果。

对数据依赖的重排序就无论如何不允许了。

深入理解Java内存模型(三):顺序一致性

如果程序是正确同步的,程序的执行将具有顺序一致性(sequentially consistent),程序的执行结果与该程序在顺序一致性模型中的执行结果相同。同步原语(lock,volatile,final)

对于未同步或未正确同步的多线程程序,JMM只提供最小安全性:线程执行时读取到的值,要么是之前某个线程写入的值,要么是默认值(0,null,false).

JMM不保证对64位的long型和double型变量的读/写操作具有原子性,而,顺序一致性模型保证对所有的内存读/写操作都具有原子性。

在这篇博文里,详细说明了总线仲裁,和在一些32位处理器上,java语言规范鼓励但不强求JVM对64位long型变量和double型变量的读/写具有原子性。

然后把一个64位long/double型变量的读/写操作拆分为两个32位的读/写操作。可能在不同的总线事务中执行,此时对这个64位变量的读/写操作将不具有原子性。

深入理解Java内存模型(四):volatile

对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

JMM针对编译器制定的volatile重排序规则表

20191210001758\_17.png

x86中,JMM仅需要在volatile写后面插入一个StoreLoad屏障即可正确实现volatile写-读的内存语义。此话要结合x86的允许的重排类型以及volatile的内存语义理解。

JSP-133专家组决定增强volatile的内存语义:严格限制编译器和处理器对volatile变量与普通变量的重排序,确保volatile的写-读和监视器的释放-获取一样,具有相同的内存语义。与监视器所比较,volatile在可伸缩性方面有优势?怎么理解这个可伸缩性?

深入理解Java内存模型(五)-锁

记忆:锁的释放-获取内存语义和volatile对象的写-读操作有相同的内存语义,

公平锁在释放-获取上和volatile的写-读有相同的内存语义

非公平锁在释放上和公平锁一样,但是获取上却使用了CAS来实现与volatile读写的内存语义,CAS,是compareAndSet

CAS同时具有volatile读和volatile写的内存语义,查实这一段我是万万看不懂。。。汗。。。

博文通过对ReentrantLock的分析,锁释放-获取的内存语义的实现至少有:

  1. 利用volatile变量的写-读所具有的内存语义。
  2. 利用CAS所附带的volatile读和volatile写的内存语义。

这两个可以整个concurrent包实现的基石中的基石。

深入理解Java内存模型(六)-final

final域的重排序限制:

20191210001758\_18.png

写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域不具有这个保障。

final引用在构造器构造前,与把这个引用赋值给一个引用变量,两操作之间不能重排序。

final引用不能从构造函数内“逸出”(个应该是要由程序员来保证)

JSR-133专家组:

只要对象是正确构造的(被构造对象的引用在构造函数中没有“逸出”),那么不需要使用同步(指lock和volatile的使用),就可以保证任意线程都能看到这个final域在构造函数中被初始化之后的值。

深入理解Java内存模型(七)-总结

常见处理器的内存模型:

  1. total store ordering TSO
  2. partial store order PSO
  3. relaxed memory order RMO 和 PowerPC内存模型

JSR-133对JDK5之前的旧内存模型的修补主要有两个:

  • 增强volatile的内存语义。旧内存模型允许volatile变量与普通变量重排序。JSR-133严格限制volatile变量与普通变量的重排序,使volatile的写-读和锁的释放-获取具有相同的内存语义。
  • 增强final的内存语义。在旧内存模型中,多次读取同一个final变量的值可能会不相同。为此,JSR-133为final增加了两个重排序规则。现在,final具有了初始化安全性。

好了,终于暂时把Java内存模型的东西回顾了一遍。加油吧骚年。

  1. 有点高大上的 Facebook Flux
  2. 明天晚上的有米科技的 python框架:Django
点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 认识JVM性能监控与故障处理工具&深入理解Java内存模型

相关推荐