持久代:不会被 gc 给轻易回收的,创建后一直存在,持久代在堆内存里面,但是不归 java 程序使用。持久代是 动态 load 的那些 class,局部变量,去 gc 其实也 gc 不了啥
1.8 之前是 Perm Gen之后 ,1.8 之后 ,非堆就变成了 mate space,叫元组区,就放 load 的那些 class。mate space用的是本地内存,1.8之前用的是虚拟内存。(metaspace是非堆内存里面)
# jstat -gcutil
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 14.74 65.26 61.44 98.03 95.33 96.791 3.201 99.992
非堆里面是栈,栈区控制的是,线程进栈出栈,控制方法的创建、运行和退出的。管调度。
jvm关注俩:堆和栈,堆里面关注 gc 的情况,栈里面关注栈的运行状态。
分代收集的好处?为啥要分什么年轻代,老年代。不分代当然是 ok 的,但是效果不好。不分代,全放一块,满了一块之后 full gc 时间太长。其实我们可以理解成, young gc 时间短,频率高, full gc 时间长。如果每次都全部堆满了再去一次性执行 full gc ,那么一次 full gc 的耗时肯定是巨长的,如果我在不用 full gc 的情况下,去进行 一些频率较高的 young gc ,某种意义程度上会增加 gc 的效率。三年不开张,开张吃三年;和细水长流的一个区别。
分析堆内存的 jmap -histo pid号,找前20开发写的方法或者类
为啥要 jmap -dump?或者说有啥好处?在 我们的 jmap -histo 分析不到,那么我们可以去 jmap -dump 把内存dump 下来,用jhat 或者 mat 分析,但是建议用 mat 去分析。核心的作用还是分析堆内存的。
线程栈是什么?线程堆栈也称线程调用堆栈,是虚拟机中线程(包括锁)状态的一个瞬间状态的快照,即系统在某一个时刻所有线程的运行状态,包括每一个线程的调用堆栈,锁的持有情况。
分析线程栈,能分析到什么?分析线程死锁以及其产生原因。
线程死锁怎么分析?看线程状态有没有 blocked / monitor 。死锁的原因,看 waiting to lock 什么,有的是锁数据库连接池,所以有可能是数据库连接池有问题
jconsole
概览
包括以下几个内容:
堆内存使用率,线程栈的线程数量,类的加载以及卸载量,最后一个是 java 进程的 cpu 使用率
内存:
注意这里执行 GC 是执行 system.gc(),是 full gc
执行一次 GC 看看现象:堆内存会下降,存活区,老年代,都会下降
young gc 时间和 full gc 的时间
有个问题,我只是开了 tomcat ,但是没跑任务,为什么 Eden 区会自己涨?为什么会自己创建对象?
是因为我们这个 jconsole 是在监控当中,是运行的状态。一开始跟开发沟通,开发也懵逼,哎哟呵,咋的了,还会自产自销?怀疑是哪里的数据连接池导致的,没有访问,是不是代码里面有心跳信息监控?后来,我们去新建了个空的 tomcat ,里面不放任何东西,也会创建对象。
为啥呢?因为这个工具 jconsole,是属于 RMI tcp 连接工具,要去给 tomcat 发心跳信息,才能获取到 jvm 内存的容量,所以会导致内存一直在涨
我们可以用命令看下:
# jstat -gcutil
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 22.95 42.80 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 43.49 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 44.16 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 44.36 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 45.05 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 45.05 74.11 97.84 95.71 1.143 0.682 1.825
这里的 Eden 区并也在涨,说明是工具的问题。
我们把工具停掉,过一会儿, Eden 区应该就不会有很大变化,所以监控最好用命令。
# jstat -gcutil
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 22.95 68.17 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 68.17 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 68.17 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 68.17 74.11 97.84 95.71 1.143 0.682 1.825
0.00 22.95 68.17 74.11 97.84 95.71 1.143 0.682 1.825
所以,我们最好不用工具,用工具的前提是:
1、出现问题,再用工具找问题
2、没问题,要写报告,用这个产出报告。
线程
在左下角,有 nio 的10个线程,是我们 tomcat 的10个真正干活的线程,我们在 tomcat 的监控页面也可以看见
tomcat 的 nio 监控页面
我们要看,也就是看这些线程
点击“检测死锁”,可以直接检测是否存在线程死锁
类
java文件 经由javac 编译成的 .class 文件 的加载峰值和加载个数。相当于 MetaSpace 。出现问题的概率比较小
概要和 MBean 也不用管
有一个比 jconsole 更强大的工具:
jvisualvm
它也是在我们 jdk 的 bin 目录下可以找到
我们先把它打开,配置好链接:
1、添加远程主机,填写好连接名,名字可以就写成 ip端口号
2、对我们刚刚添加的远程连接,添加一个 JMX 连接,注意这里连接内 要加入 我们配置好的 jconsole 端口号,点击确定即可
3、双击打开就成,选择“监视”
可以看到有四个小块:CPU、内存、类、线程
4、一般来讲功能很少,可以下插件:可在工具内安装,我这里是因为安装完了
有时候安装会不成功,那么是因为源错误了,下载不了,换一个即可。选择到插件中心,填入合适的URL。
插件中心的地址,从:http://visualvm.github.io/pluginscenters.html 去获取对应版本的URL(网页响应较慢)
5、安装完插件,就可以比较系统地分析 JVM 了。
监视
我们可以看到, CPU这个选项内,有两个东西:CPU 使用情况和垃圾回收活动。说明在 1.8 版本之后,把 gc 耗费的 CPU 拎出来做了个曲线(因为 GC 确实很耗费 CPU)
堆/Metaspace
这里黄色的代表总大小,蓝色代表已经使用的
比如我们去刷新那个 加压的 url http://47.107.183.88:8080/test1/init1.jsp,那么这里的堆内存是没有回收完全的没说明确实存在内存泄漏
内存区块是没有 jconsole 展示得好的,jconsole 是有 Eden 区,老年代那些内存的展示
线程
1、线程可视化有以下几种线程状态:running、sleeping、waiting、time_waiting、monitor状态
下图有几个时间要注意下:运行时间和总计时间。总计时间是什么?java 进程总共耗费多少时间片,其实没多大意义
Thread inspector内,是线程栈的状态信息,跟 jstack 一样的内容。这里也可以 dump ,就相当于,jstack -dump 命令,可以dump下来在这工具内查看
dump 之后:多了一个 Thread dump
抽样器
分析 cpu 和内存的前提条件是 这俩有问题,cpu高或者内存溢出,我们这里先对 cpu 进行抽样
这里抽取cpu :是 哪个线程占用的 cpu 资源可以观察到
问题:
经常会被问到的问题,我们做性能测试,有时候测试的 tps 是这两种曲线:
正常来讲是应该为 a 这种曲线,那为啥会出现 b 这种呢?为啥跑到最大值,反而下降,未恒定住?
这时候请求过来多了,服务器处理不了 ,cpu 开始抢占,发生太多的 上下文切换,所以消耗在 内核的 cpu 很多,%sys 肯定会很高,导致供用户态 cpu 的时间片减少,服务器处理能力急剧下降
还有可能是一种抖动的曲线,肯定就是 gc ,因为 gc 过程中,程序暂停运行,处理能力肯定就下降了
抽取内存
再抽取过程中,我们刷新压测页面
抽取出来的内存快照信息如下:可以看到cn.test.TestBean占用大量内存
jconsole plugins 要引入 JOPR.jar文件才可以显示
装入
还有文件装入的功能,比如说,我们进行了 jmap-dump,进行堆内存分析,就可以将其装入到该工具分析,之前是放进 mat 分析
切换到类视图。可以看到,之前的 dump 文件内。该类的实例数,大小等数据,可以判断是哪个类占用了大量的内存,跟我们的 jmap -histo 一样
双击类进去,可以知道里面具体的实现方式以及分析占内存原因
总结,这工具整体上不是很好用
jprofiler
java 应用程序性能的神器。很多人依赖这个工具。
一开始这个 Memory View ,就是我们的 jmap -histo下的显示,类名,实例数、占内存大小
我们可以依赖这个页面来分析堆内存溢出的类,先 Mark Current 一下,代表将当前内存的占用情况做一个标记,
标记之后,所有的内存区会变成绿色,增量会变成紫色
如此一来,我们可以去刷新压测的页面,看看 Mark 前后,内存的变化情况
我们只刷新了 20 下,所以变化不是很大,但是相对其他类来讲,还是增长了很大的,紫色部分就是 mark 后的新增对象
这样我们在压测过程中,mark 一下,这个类一直增加或者说减小的幅度特别小,那么说明它就是导致内存溢出的原因。
如果这样还不足以说明问题,我们可以强制 run gc ,看它有没有减少
可以看到,强制 gc 该类依旧没有被回收掉,其他的都被回收了。说明这的确是个顽固分子
那这个类,我们要怎么去分析它的调用链呢?把它放进 heap walker 进行分析
提示不用管,直接 ok
静静地等待,可以去泡杯咖啡……
切换到, big objects。看这个类的大对象,引用情况。然后选中一个大对象,选择“ show in graph”,用图表方式展开,分析
我们可以看到:
cn.test.TestBean 的子类是 java.lang.String,父类是 java.lang.Object[]
点 里面的 ‘+’ 号,就可以看上面的引用关系
这就很清楚地能看到 ,完整的调用链。其实定位到类就行了。依据我自己的的猜想,就是看俩黄色的部分,一个是页面,也个是类。主要是看引用关系
在 CPU view 内,可以看线程占用 cpu 的情况,但是我这里好像没有数据展示。这里的 cpu 指的是 jvm cpu 内的,而不是整个服务器的 cpu
这里抓取 cpu是比较敏感的,刷新压测页面,可以得到很好反馈,可以说明是哪个线程占用的 cpu 。而且我们可以发现,这个线程是调用 /test1/init1.jsp这个页面导致的。其实不光能到jsp页面,还能抓到哪个方法,如果是 sql 导致的,还能抓到是哪个 sql 语句导致的。
例子如下:
总结:分析 堆 内存高的原因以及 CPU 使用高的原因
JVM(下)的更多相关文章
-
Spark Tungsten揭秘 Day1 jvm下的性能优化
Spark Tungsten揭秘 Day1 jvm下的性能优化 今天开始谈下Tungsten,首先我们需要了解下其背后是符合了什么样的规律. jvm对分布式天生支持 整个Spark分布式系统是建立在分 ...
-
Hotspot JVM下,parallel与concurrent的区别
转载于知乎 作者:Ted Mosby链接:https://www.zhihu.com/question/21535747/answer/144884632来源:知乎著作权归作者所有.商业转载请联系作者 ...
-
大型Java进阶专题(十一) 深入理解JVM (下)
前言 前面我们了解了JVM相关的理论知识,这章节主要从实战方面,去解读JVM. 类加载机制 Java源代码经过编译器编译成字节码之后,最终都需要加载到虚拟机之后才能运行.虚拟机把描述类的数据从 ...
-
转载:Tomcat的JVM设置和连接数设置
Windows环境下修改“%TOMCAT_HOME%\bin\catalina.bat”文件,在文件开头增加如下设置:set JAVA_OPTS=-Xms256m -Xmx512m Linux环境下修 ...
-
ubuntu 16.04下安装oracle jdk 1.7
网上搜索了下,知道了大概,不能用apt装了,oracle也不再提供deb包了.只能下tar.gz包自己装. 先下载下来jdk:http://www.oracle.com/technetwork/jav ...
-
linux ubuntu下如何安装并且切换java版本(Unsupported major.minor version 52.0)
最近在做一个dcos(数据中心操作系统)的东西,需要用marathon来做进程管理.遗憾的是0.6版本的marathon在API方面很是缺少,换成了0.15版本之后,运行时提示“Unsupported ...
-
JVM——深入分析对象的内存布局
概述 一个对象本身的内在结构需要一种描述方式,这个描述信息是以字节码的方法存储在方法区中的.Class本身就是一个对象,都以KB为单位,如果new Integer()为了表示一个数据就占用KB级别的内 ...
-
Tomcat之jvm及连接数设置
一.Tomcat的JVM提示内存溢出 查看%TOMCAT_HOME%\logs文件夹下,日志文件是否有内存溢出错误 二.修改Tomcat的JVM 1.错误提示:java.lang.OutOfMemor ...
-
终极锁实战:单JVM锁+分布式锁
目录 1.前言 2.单JVM锁 3.分布式锁 4.总结 =========正文分割线================= 1.前言 锁就像一把钥匙,需要加锁的代码就像一个房间.出现互斥操作的场景:多人同 ...
随机推荐
-
oracle 存储过程创建及执行简单实例
1. 创建 CREATE OR REPLACE PROCEDURE getAplage(eNo IN NUMBER,salary OUT NUMBER) AS BEGIN SELECT AplAge ...
-
java语法基础
Java的基本符号(token) Java的单词符号有五种:关键字.标识符.常量.分隔符和操作符. Java的字符集 Java 采用一种称为unicode的字符集,该字符集合是一种新的编码标准,与常见 ...
-
Vue混合
gitHub地址: https://github.com/lily1010/vue_learn/tree/master/lesson13 一 定位 混合以一种灵活的方式为组件提供分布复用功能.混合对象 ...
-
python 练习 8
#!/usr/bin/python # -*- coding: utf-8 -*- def ntom(x,size,mod): t=[0]*(size) j=0 while x and j<si ...
-
Python补充03 Python内置函数清单
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明. Python内置(built-in)函数随着python解释器的运行而创建.在Pytho ...
-
MBR所在位置
如果offset的000000000位置如下图所示(主要看红色框框位置是否出现NTFS字样),说明系统文件是NTFS "EB 52"至"55 AA"位置是MBR ...
-
hdu 1284 关于钱币兑换的一系列问题 九度oj 题目1408:吃豆机器人
钱币兑换问题 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
-
【Python中if __name__ == &#39;__main__&#39;: 的解析】
在很多Python代码中,在代码的最下方会看到 if __name__ == '__main__':,这段代码到底有什么用呢? 在理解这个语句的作用前,需要知道的是,一般的Python文件后缀为.p ...
-
C语言之链表的使用
C语言链表初学者都说很难,今天就来为大家讲讲链表 讲链表之前不得不介绍一下结构体,在链表学习之前大家都应该已经学了结构体,都知道结构体里面能有许多变量,每个变量可以当做这个结构体的属性,例如: str ...
-
python生成器、装饰器、正则
包子来了[4],被[mayun]吃了! 包子来了[4],被[mahuateng]吃了! 做了两个包子 包子来了[5],被[mayun]吃了! 包子来了[5],被[mahuateng]吃了! 做了两个包 ...