一、监控jvm的GC情况
1,监控jvm的GC情况 jstat gcutil pid 1000 100 2, jmap -heap pid:可以看到java进程的堆的配置信息,各区的空间大小和配置信息 jmap -histo 5279 | head -20:查看jvm中各个类的实例数、占用内存数量以及类的全名 jmap -dump:format=b,file=m.hdump 17777:对堆内存进行dump,以文件的形式进行保存下来,可以用jvisualvm等工具对文件进行分析
二、内存泄露有什么现象
1,tps出现大幅波动,并慢慢降低,甚至降为0,响应时间随之波动,慢慢升高 2,通过jstat命令看到,Jvm中Old区不断增加,FullGC非常频繁,对应的FullGC消耗的时间也不断增加 3,通过jconsole/jvisualvm可以看到,堆内存曲线不断上升,接近上限时,变成一条直线 内存泄露怎么定位 1,通过jmap命令:jmap -histo pid | head -20,查看当前堆内存中实例数和占用内存最多的前20个对象 2,通过jvisualvm,进行远程堆dump,然后把dump文件下载下来,用jvisualvm打开进行分析,可以看到更直观的jvm中对象的信息 在什么样的场景下监控内存泄露问题 1,在试压阶段,或任意场景都可以考虑通过jconsole和jstat监控jvm的情况 2,在稳定性场景中,一定要关注Jvm内存使用的情况,在长时间的压测下,最容易看出内存泄露的问题
三、内存泄漏的案例
内存泄漏的案例,先把tomcat的session持久化取消掉 修改:tomcat目录下temp/context.xml,将<Manager pathname=“”/>这行注释打开 JPROFILER工具下载地址:http://www.ej-technologies.com/download/jprofiler/files linux下安装:rpm -ivh jprofiler_linux_9_1_1.rpm 需要在tomcat/bin/catalina.sh文件配置jvm参数的地方添加 -agentpath:/opt/jprofiler9/bin/linux-x86/libjprofilerti.so=port=8849,nowait
四、
potlight监控 需要新建一个用户useradd xxx,修改密码:passwd xxx Linux下网卡的配置参数:ethtool eth0,Speed: 1000Mb/s,说明当前网卡的上限是1000/8=125MB Linux监控命令:详细命令见word文档 cpu监控:top 内存监控:free -m 磁盘繁忙度监控:iostat -x -k 1 磁盘空间监控:df -h 网络监控:nmon 其他监控:vmstat Linux磁盘速度测试:dd if=/dev/zero of=/export/ddtest bs=8k count=1000000 oflag=direct ========================================================================================= 1,nmon 命令:./nmon -fT -s 10 c 100,每10s采样一次,一共采样100次,并在当前目录下生成监控结果文件 重点关注四项 cpu:cpu_all disk:diskbusy memory的使用百分比:(total-free-buffer-cache)/total netIO:eth0-total 2,spotlight监控 ========================================================================================= 线程的五种状态 * 新建:new * 运行:runnable * 等待:waitting(无限期等待),timed waitting(限期等待) * 阻塞:blocked * 结束:terminated 线程的两种监控方式: 构造线程状态的Java脚本:java -jar thread-test.jar [1|2|3|4] 构造不同的状态的线程 1:runnable 2:waitting 3:timedWaitting 4:blocked 2,jvisualvm,图形界面的方式 jvisualvm 监控之前先对jvm加监控参数,在tomcat的bin目录下,catalina.sh文件中,搜索JAVA_OPTS=,在if里面,添加: -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.210.45 1-改port 2-改hostname为本机ip 3,jstack pid:命令行方式 "Mytesting..." prio=10 tid=0xb7545800 nid=0x15cf waiting for monitor entry [0xb4ba8000..0xb4ba9070] java.lang.Thread.State: BLOCKED (on object monitor) at ThreadStatusTest.blocked(ThreadStatusTest.java:53) - waiting to lock <0x8bd6a238> (a java.lang.Object) at BlockedStatus.run(ThreadStatusTest.java:73) at java.lang.Thread.run(Thread.java:619) ====================================================================================== 4,top、iostat、vmstat命令见文档 [root@zhoucentos mysql]# free -m total used free shared buffers cached Mem: 498 491 7 0 14 32 -/+ buffers/cache: 443 55 Swap: 2015 97 1918 第一行代表是从系统的角度来统计的数据,其中的used包含了buffers和cached 第二行是代表从应用程序的角度来统计的数据,used是真正应用程序占用的内存 第一行的used = 第二行的used+buffers+cached 连接数的查看命令:netstat -an|grep 8080 |wc -l redis:主要关注网络流量和连接数,通过netstat命令或者info命令可以查看连接数 1,慢查询 show variables like '%slow%' show global status like '%long_query%' 具体配置见文档 mysqldumpslow -s at 10 slow.log 2,连接数 show variables like '%connections%' show status like '%thread%' 其中: Threads_connected 当前打开的连接的数量 Threads_cached 线程缓存内的线程的数量 Threads_created 创建的线程数 Threads_running 激活的(非睡眠状态)线程数 show status like '%connection%' Connections 试图连接MySQL服务器的次数 max_connections:整个MySQL允许的最大连接数; 3,缓冲池 show variables like '%buffer_pool_size%' show status like '%buffer%'; 其中: Innodb_buffer_pool_reads:缓冲池中没有读到数据,而从磁盘内读取的次数 Innodb_buffer_pool_read_requests:来缓冲池中读数据的次数 Innodb_buffer_pool_pages_total:缓冲池的总页数(内存是以页为单位) Innodb_buffer_pool_pages_free:缓冲池中处于空闲状态的页数 命中率:innodb_buffer_read_hits=(1-Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests)*100% 以上要掌握 ================================================================= show variables like '%query_cache%'; show status like '%Qcache%' Query_cache_hits=(Qcache_hits/(Qcache_hits+Qcache_inserts))*100% 5,线程缓存(连接池)-- 了解 show variables like '%thread%' show status like 'connections' show status like '%thread%' Threads_Cache_Hit=(Connections-Threads_created)/Connections*100% 6,表锁和行锁 show status like '%lock%'; 存储引擎:早期mysql用的,MyISAM,现在都用Innodb MyISAM:支持表锁 Innodb:支持行锁
五、
Jvm常用参数 --------------------------------------- 堆内存 = 年轻代+老年代+永久代 年轻代 = Eden+Survivor Survivor = From Space+To Space --------------------------------------- 年轻代 = Eden+From Space+To Space 堆内存=Eden+From Space+To Space+老年代+永久代 ==================================== -Xms2048m,初始堆大小,建议<物理内存的1/4,默认值为物理内存的1/64 -Xmx2048m,最大堆大小,建议与-Xms保持一致,默认值为物理内存的1/4 -Xmn512m,新生代大小,建议不超过堆内存的1/2 -Xss256k,线程堆栈大小,建议256k -XX:PermSize=256m,永久代初始值,默认值为物理内存的1/64 -XX:MaxPermSize=256m,永久代最大值,默认值为物理内存的1/4 -XX:SurvivorRatio=8:年轻带中Eden区和Survivor区的比例,默认为8:1,即Eden(8),From Space(1),ToSpace(1) -XX:MaxTenuringThreshold=15:晋升到老年代的对象年龄,每个对象坚持过一次MinorGC后对象年龄+1,默认值是15,年龄超过15进入到老年代,该参数在串行GC时有效 -XX:PretenureSizeThreshold=3145728:单位字节,只对Serial和ParNew两款收集器有效,新生代采用Parallel Scavenge GC时无效,大于这个值的对象直接在老年代进行分配 非稳定参数,使用方式主要有以下三种: 1,-XX +<option>:开启option参数 2,-XX -<option>:关闭option参数 3,-XX <option>=<value>:将option参数的值设置为value client模式,Jvm默认垃圾收集器 UseSerialGC:新生代采用Serial收集器,老年代采用Serial Old收集器 server模式,Jvm默认垃圾回收期 UseParallelGc:新生代采用Parallel Scavenge收集器,吞吐量优先的收集器,老年代采用Serial Old收集器 -XX:+UseConcMarkSweepGC:默认关闭,ParNew+CMS+Serial Old,当CMS收集器出现ConcurrentModeFailure错误(Jvm预留空间不足以容纳程序使用),采用后备收集器Serial Old CMS相关参数 -XX:CMSInitiatingOccupancyFraction=80:CMS收集器在老年代空间被使用多少时触发FullGC,默认为92 -XX:+UseCMSCompactAtFullCollection:CMS收集器在FullGC时开启内存碎片的压缩,默认开启 -XX:CMSFullGCsBeforeCompaction=8:执行多少次不压缩FullGC后,进行一次压缩,默认是0(代表每次FullGC都进行压缩) -XX:+UseCMSInitiatingOccupancyOnly:使用手动定义初始化定义开始,禁止hostspot自行触发CMS GC -XX:ParallelGCThreads=8:并行收集器的线程数,此值最好配置与处理器数目相等 同样适用于CMS 日志参数: -XX:+HeapDumpOnOutOfMemoryError:当发生内存溢出时,进行堆内存dump -XX:+PrintGCDetails:打印GC的详细信息 ====================================================================== 企业实际配置 -server -Xms1028m -Xmx1028m -XX:PermSize=256m -XX:MaxPermSize=256m -Xmn512m -XX:MaxDirectMemorySize=1g -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=5000 -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=8 -Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs/java.hprof
六、
案例:慢sql和执行计划 http://192.168.0.110:8080/PerfTeach/SlowQuery?cardNO=1000009 http://192.168.0.110:8080/PerfTeach/SlowQuery?cardNO=1000009&course=Math 现象:tps很低,响应时间很长,数据库服务器cpu很高(接近100%),应用服务器负载比较低 慢查询的配置和抓取:略 执行计划 在sql语句前加上explain,可以分析这条sql语句的执行情况 explain select * from teacher where Type列可能的值: Const:表中只有一个匹配行,用到primary key或unique key Eq_ref:唯一性索引扫描,key的所有部分被连接联接查询使用,且key是unique或primary key ref:非唯一性索引扫描,或只使用了联合索引的最左前缀 Range:索引范围扫描,在索引列上进行给定范围内的检索,如between,in(1,100) Index:遍历索引... All:全表扫描 Prossible key:使用哪个索引能找到行 Keys:sql语句使用的索引 rows:mysql 根据索引选择情况,估算查找数据所需读取的行数 --------------------------------------------------- 案例:线程死锁 http://192.168.1.110:8080/PerfTeach/DeadServlet 线程死锁就是有两个线程,一个线程锁住了资源A,又想去锁定资源B,另外一个线程锁定了资源B,又想去锁定资源A,两个线程都想去得到对方的资源,而又不愿释放自己的资源 从而造成一种互相等待,无法执行的情况 现象:出现死锁后,tps降为0,压力测试工具无法得到服务器的响应,服务器硬件资源空闲,通过jvisualvm去查看线程情况,至少两个线程一直处于红色的阻塞状态 死锁经常表现为程序的停顿,或者不再响应用户的请求。从操作系统上观察,对应进程的CPU占用率为零 定位方法:通过jvisualvm或者jstack,进行线程dump,对现在状态进行分析,获取到哪行代码导致的死锁,如: Found one Java-level deadlock: http-bio-8080"-exec-162": waiting to lock monitor 0x0818abac (object 0x84b40ad0, a com.lee.domain.Order), which is held by ""http-bio-8080"-exec-158" ""http-bio-8080"-exec-158": waiting to lock monitor 0x08188cd0 (object 0x84b3bc48, a com.lee.domain.Order), which is held by ""http-bio-8080"-exec-162" Java stack information for the threads listed above: 死锁一般解决思路: 减少嵌套加锁 减小锁粒度 --------------------------------------------------- 线程info信息块: 1. "Timer-0" daemon prio=10tid=0xac190c00 nid=0xaef in Object.wait() [0xae77d000] 2. java.lang.Thread.State: TIMED_WAITING (on object monitor) 3. atjava.lang.Object.wait(Native Method) 4. -waiting on <0xb3885f60> (a java.util.TaskQueue) ###继续wait 5. atjava.util.TimerThread.mainLoop(Timer.java:509) 6. -locked <0xb3885f60> (a java.util.TaskQueue) ###已经locked 7. atjava.util.TimerThread.run(Timer.java:462) * 线程名称:Timer-0 * 线程类型:daemon * 优先级: 10,默认是5 * jvm线程id:tid=0xac190c00,jvm内部线程的唯一标识(通过java.lang.Thread.getId()获取,通常用自增方式实现。) * 对应系统线程id(NativeThread ID):nid=0xaef,和top命令查看的线程pid对应,不过一个是10进制,一个是16进制。(通过命令:top -H -p pid,可以查看该进程的所有线程信息) * 线程状态:in Object.wait(). * 起始栈地址:[0xae77d000] * Java thread statck trace:是上面2-7行的信息。到目前为止这是最重要的数据,Java stack trace提供了大部分信息来精确定位问题根源。 --------------------------------------------------- 性能瓶颈定位整体思路,从前到后,从表象到内部 1,首先排除压力机的性能情况,包括cpu、内存 2,应用服务器的硬件指标,cpu,内存,网络IO(ethtool eth0),磁盘IO 3,其他服务器,比如数据库服务器,依赖的其他应用服务器 4,看应用服务器的日志,tomcat下看的是catalina.out,用tail -200 catalina.out ,看cause by:xxx TimeOut 5,tps比较低,应用服务器cpu比较高,jvisualvm看下java进程,消耗cpu的方法 6,tps比较低,应用服务器cpu也比较低,可能是java程序的线程阻塞或死锁,也用jvisualvm看,如果红色的线程比较多,就是阻塞的原因,jstack pid 7,tps比较低,响应时间比较长,首先要先弄清楚请求的流向,比如:LR-->nginx-->tomcat-->java-->数据库-->缓存-->外部依赖--返回,如果排除到其他的问题 可以在java代码加时间戳的方法来定位响应时间比较长的问题 8,tps缓慢降低,或大幅降低,tps大幅波动,用jmap或者jvisualvm进行堆内存dump,用jstat -gcutil,看看是否有内存泄露趋势,Jvm的FullGC是否频繁 9,排查连接数相关的内容,包含tomcat的连接数、mysql的连接数,某端口的连接数:netstat -an|grep 8080 | wc -l 关于连接数,要看2方面,首先是中间件最大配置的连接数,然后就是当前连接数 tomcat的当前连接数用netstat可以看,略。最大连接数在 一般tomcat需要设置的参数,第一个就是连接数在conf/server.xml里配置 <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> maxThreads="200" 表示最多同时处理200个连接,默认值是200 acceptCount="100" 当同时连接的人数达到maxThreads时,还可以接收排队的连接,超过这个连接的则直接返回拒绝连接。默认值为100 第二个jvm的参数,在bin目录下的catalina.sh if [ -z "$LOGGING_MANAGER" ]; then JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.1.117 -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xms512m -Xmx512m -Xmn128m -XX:MaxPermSize=64m" else JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER" --------------------------------------------------- 线程阻塞问题-log4j日志级别问题 http://192.168.1.110:8080/PerfTeach/Block?userId=1 log4j.xml中设置的日志级别,程序会打印在此级别之上日志 Error Warn Info debug 级别越低,日志越多 ------------------------------------------------- tomcat连接数设置 netstat -an|grep 8080 |wc -l :查看8080当前端口建立的连接数 maxThreads="200" 表示最多同时处理200个连接,默认值是200 minSpareThreads="10" 表示即使没有人使用也开这么多空线程等待,默认是10 (tomcat7可能不支持)maxSpareThreads="75" 表示如果最多可以空75个线程,例如某时刻有80人访问,之后没有人访问了,则tomcat不会保留80个空线程,而是关闭5个空的。 acceptCount="100" 当同时连接的人数达到maxThreads时,还可以接收排队的连接,超过这个连接的则直接返回拒绝连接。默认值为100 ---------------------------------------------- 响应时间长 http://192.168.1.110:8080/PerfTeach/TimeMonitor?id=1 ------------------------------------------------- 数据库架构的调优 --读写分离:主从配置 --分库分表:根据一个id来路由库名和表名,id%10 user_1 user_2 user_3 ... user_10 --硬件调优 普通磁盘 SSD fusionIO卡 6,应用程序架构调优 --同步-异步 --加缓存 --循环调用和批量调用 接口1,单条数据查询 for (){ 调用接口1 } 接口2 调用接口2 --分布式集群部署,加实例,加机器