虚拟机+调优视频
JVM性能调优教程
一、进程与线程区别
进程是资源分配的最小单位,线程是CPU调度的最小单位
做个简单的比喻:进程=火车,线程=车厢
- 线程在进程下行进(单纯的车厢无法运行)
- 一个进程可以包含多个线程(一辆火车可以有多个车厢)
- 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
- 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
- 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
- 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
- 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
- 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
- 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”
jstack <pid>,可用于查看java进程idjstack -l pid > jstack.log 使用命令将线程信息存入文件中
二、基本概念
1、jstack命令的语法格式:jstack <pid>,可用于查看java进程id。
2、Dump文件:Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。Dump文件是用来给驱动程序编写人员调试驱动程序用的,这种文件必须用专门的工具软件打开,比如使用Windbg。
在Windbg中可以通过.dump命令保存进程的dump文件。比如下面的命令把当前进程的镜像保存为c:\testdump.dmp文件:.dump /ma c:\testdump.dmp。
其中、ma参数表示dump文件应该包含进程的完整信息,包括整个用户态的内存,这样dump文件尺寸会比较大,信息非常全面。如果不是用、ma参数,保存下来的dump文件只包含了部分重要资料,比如寄存器和线程栈空间,文件尺寸会比较小,无法分析所有的数据。
3、java线程Dump:线程dump是非常有用的诊断java应用问题的工具,每一个java虚拟机都有及时生成显示所有线程在某一点状态的线程dump的能力。虽然各个java虚拟机线程dump打印输出格式上略微有一些不同,但是线程dump出来的信息包含线程基本信息;线程的运行状态、标识和调用的堆栈;调用的堆栈包含完整的类名,所执行的方法,如果可能的话还有源代码的行数。
JVM中的许多问题都可以使用线程dump文件来进行诊断,其中比较典型的包括线程阻塞,CPU使用率过高,JVM Crash,堆内存不足和类装载等问题。
一般情况下,通过jstack输出的线程信息主要包括:jvm自身线程、用户线程等。其中jvm线程会在jvm启动时就会存在。对于用户线程则是在用户访问时才会生成。
三、dump内容介绍
1、组成
标注daemon字样的是后台线程
1、线程的一些基本信息:名称、优先级及id
2、tid: java内的线程id,vm线程id:tid=0x000000000729a000,jvm内部线程的唯一标识(通过java.lang.Thread.getId()获取,通常用自增方式实现。)
3、nid: 操作系统级别线程的线程id
4、prio: java内定义的线程的优先级5、os_prio:操作系统级别的优先级
6、线程状态:waiting on condition等
7、线程的调用栈
8、线程锁住的资源:locked<0x3f63d600>
2、nid信息:
在thread dump中每个线程都有一个nid,nid(16进制)转十进制,就是对应在linux系统中获取到的线程id
方法一:PS
在ps命令中,“-T”选项可以开启线程查看。下面的命令列出了由进程号为<pid>的进程创建的所有线程。
ps -T -p <pid>
“SID”栏表示线程ID,而“CMD”栏则显示了线程名称。
方法二: Top
top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。
top -H
要让top输出某个特定进程<pid>并检查该进程内运行的线程状况:
top -H -p <pid>
这时pid就是线程id
二、Monitor(监视器)
进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁。如果对象未被锁住,则迚入拥有者;否则则在进入区等待。一旦对象锁被其他线程释放,立即参与竞争。
拥有者(The Owner):表示某一线程成功竞争到对象锁。
等待区(Wait Set) :表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。
3、调用修饰
表示线程在方法调用时额外的重要操作。线程dump分析的重要信息。修饰上方的方法调用。
java锁之wait,notify
1、locked<地址>目标:使用synchronized申请对象锁成功,监视器的拥有者;
2、waiting to lock<地址>目标:使用synchronized申请对象锁未成功,在进入区等待;
3、waiting on<地址>目标:使用synchronized申请对象锁成功后,调用了wait方法,进入对象的等待区等待唤醒。在调用栈顶出线,线程状态为WAITING或TIMED_WAITING;
4、parking to wait for<地址>目标:park是基本的线程阻塞原语,不通过监视器在对象上阻塞。随concurrent包出现的新的机制,与synchronized体系不同;例子:需与堆栈中的"parking to wait for (atjava.util.concurrent.SynchronousQueue$TransferStack)"结合来看。首先此线程是在等待某个条件的发生,来把自己唤醒。其次SynchronousQueue不是一个队列,其是线程之间移交信息的机制,当我们把一个元素放入到 SynchronousQueue 中时必须有另一个线程正在等待接受移交的任务,因此这就是本线程在等待的条件。
4、线程状态
想要通过jstack命令来分析线程的情况的话,首先要知道线程都有哪些状态,下面这些状态是我们使用jstack命令查看线程堆栈信息时可能会看到的线程的几种状态:
1、NEW:线程刚刚被创建,但是还没有调用start()方法,不会出现在Dump中。
2、RUNNABLE:在虚拟机内执行的。
3、BLOCKED:受阻塞并等待监视器锁。
4、WATING:无限期等待另一个线程执行特定操作。调用以下方法会导致:
Object.wait with no timeout
Thread.join with no timeout
LockSupport.park
5、TIMED_WATING:有时限的等待另一个线程的特定操作。调用以下方法会导致:
Thread.sleep
Object.wait with timeout
Thread.join with timeout
LockSupport.parkNanos
LockSupport.parkUntil
6、TERMINATED:已退出的。
状态转化图

线程状态例子分析
5、线程动作:
线程状态产生的原因:
1、runnable:状态一般为RUNNABLE,表示线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。一个单核CPU在同一时刻,只能运行一个线程。
2、in Object.wait():等待区等待,状态为WAITING或TIMED_WAITING。
3、waiting for monitor entry:进入区等待,状态为BLOCKED。
4、waiting on condition:等待去等待,被park。
5、sleeping:休眠的线程,调用了Thread.sleep()。
Wait on condition 该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。 最常见的情况就是线程处于sleep状态,等待被唤醒。 常见的情况还有等待网络IO:在java引入nio之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。
6、遇到线程问题时如何分析
wait on monitor entry: 被阻塞的,肯定有问题
runnable : 注意IO线程
in Object.wait(): 注意非线程池等待
7、死锁分析例子

8、热锁分析
热锁,也往往是导致系统性能瓶颈的主要因素。其表现特征为,由于多个线程对临界区,或者锁的竞争,可能出现:
* 频繁的线程的上下文切换:从操作系统对线程的调度来看,当 线程在等待资源而阻塞的时候,操作系统会将之切换出来,放到等待的队列,当线程获得资源之后,调度算法会将这个线程切换进去,放到执行队列中。
* 大量的系统调用:因为线程的上下文切换,以及热锁的竞争,或 者临界区的频繁的进出,都可能导致大量的系统调用。
9、其他
虚拟机执行Full GC时,会阻塞所有的用户线程。因此,即时获取到同步锁的线程也有可能被阻塞。 在查看线程Dump时,首先查看内存使用情况。
转载:
【JVM性能调优】jstack和线程dump分析
java命令--jstack 工具 查看JVM堆栈信息
四、JVisualVM
转: JVisualVM的使用教程
1、visual gc
(1)、安装visual gc
使用之前,我们需要安装一个插件,来更好的来观察虚拟机的性能,点击上方的工具-插件

在可用插件那里选择下载,安装一个VIsual GC的插件

一般会报错,因为默认的链接已经给转移了,需要在设置那里把默认的链接更改
点击设置,编辑,把URL更改一下


那URL填什么呢?先确定一下自己的jdk版本号,然后用以下链接去查看URL
确认版本号,可以菜单键+R,执行cmd,输入java -version来查看自己的版本号

比如我的是201

比如我的是JDK8的201,所以应该是131-291之间,所以我就复制下面那行蓝色的URL到设置的定制器中

然后就可以下载想要的插件啦
然后重启一下即可看到有visual GC这个选项了
可以看到GC time是指发生了多少次的GC,图中就是发生了233次GC,就花了276.256ms的时间,而下一行的Eden区,也是发生了223次GC,花费的时间也是276.256ms,很显然,发生的GC都是在Eden区,Old老年代区发生了0次GC,花费0s。
(2)、Visval gc 介绍
装载:visualvm 插件 visual gc 使用介绍
visual gc 工具分成三大块
- the Visual GC window
- the Graph window,
- the Survivor Age Histogram window(可选的)
第一块:Visual GC Window
Spaces 就是 Visual GC window 了。它会分成 3 个竖直的部分,
分别是 Perm 永生代, Old 老年代和新生代。
新生代又分成 3 个部分 Eden 区, S0 survivor 区, S1 survivor 区.
每个方框中都使用不同的颜色表示,其中有颜色的区域是占用的空间,空白的部分是指剩余的空间。
当程序正在运行时,该部分区域就会动态显示,以直观的形式显示各个分区的动态情况。

第二块:Graph Window
该区域包含多个以时间为横坐标的状态面板。
1、Compile Time

编译时间表示虚拟机的 JIT 编译器编译热点代码的耗时。
Java 语言为了实现跨平台特性, Java 代码编译出来后形成的 class 文件中存储的是 byte code,jvm 通过解释的方式形成字节码命令,这种方式与 C/C++ 编译成二进制的方式 相比要慢不少。为了解决程序解释执行的速度问题, jvm 中内置了两个运行时编译器,如果一段 Java 代码被调用达到一定次数,就会判定这段代码为热点代码(hot spot code),并将这段代 码交给 JIT 编译器编译成本地代码,从而提高运行速度。所以随着代码被编译的越来越彻底,运行速度应当是越来越快。而 Java 运行器编译的最大缺点就是它进行编译时需要消耗程序正常的运行时间,也就是 compile time.
2、Class Loader Time

表示 class 的 load 和 unload 时间
3、GC Time

22 collections 表示自监视以来一共经历了 22 次GC, 包括 Minor GC 和 Full GC
2.030s 表示 gc 共花费了 2.030s
Last Cause: Allocation Failure 表示上次发生 gc 的原因: 内存分配失败
4、Eden Space

Eden Space (340.500M,185.000M): 91.012M
表示 Eden Space 最大可分配空间 340.500M
Eden Space 当前分配空间 185.000M
Eden Space 当前占用空间 91.012M
21 collections, 1.012s
表示当前新生代发生 GC 的次数为 21 次, 共占用时间 1.012s
5、Survivor 0 and Survivor 1

S0 和 S1 肯定有一个是空闲的,这样才能方便执行 minor GC 的操作,但是两者的最大分配空间是相同的。并且在 minor GC 时,会发生 S0 和S1 之间的切换。
Survivor 1 (113.500M, 75.000M) : 36.590M
表示 S1 最大分配空间 113.500M, 当前分配空间 75.000M, 已占用空间 36.590M
6、Old Gen

Old Gen (682.500M, 506.500M) : 233.038M, 1 collections, 1.018s
(682.500M, 506.500M) : 233.038M
表示 OldGen 最大分配空间 682.500M, 当前空间 506.500M, 已占用空间 233.038M
1 collections, 1.018s 表示老年代共发生了 1次 GC, 耗费了 1.018s 的时间。
老年代 GC 也叫做 Full GC, 因为在老年代 GC 时总是会伴随着 Minor GC, 合起来就称为 Full GC。
7、Perm Gen

Perm Gen (256.000M, 227.500M) : 122.800M
256.000M 表示最大可用空间,可以使用 -XX:MaxPermSize 指定永久代最大上限
227.500M 表示当前永久代空间
122.800M 表示永久代当前占用空间
对 HotSpot 虚拟机来说,可以把永久代直接等同于方法区,其中会存储已经被jvm 加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/55541.html