JVM工具介绍与使用

目录
  1. ✔jps命令
  2. ✔jstat命令
  3. ✔jmap命令
  4. ✔jstack打印java进程的堆栈信息
  5. ✔图形化工具MAT(memory analyze tool)
  6. ✔总结

jps命令

介绍:查看所有具有权限的java进程的具体状态:包括进程ID,进程启动路径,以及进程启动参数。

使用格式:

1
jps [options] [hostid]

常用参数:

1
-l #输出应用程序主类的完整包名,或者是应用程序JAR文件的完整路径。
-v #输出传给JVM的参数。

使用:

1
root@ip-10-21-151-113:~# jps
59117 WatchdogManager
59171 Resin
15374 Jps
1
root@ip-10-21-151-113:~# jps -v
59117 WatchdogManager -Dresin.watchdog=a -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl -Djava.awt.headless=true -Dresin.home=/usr/local/resin/ -Dresin.root=/usr/local/resin -Xrs -Xss256k -Xmx32m
15401 Jps -Dapplication.home=/opt/jdk1.7.0_45 -Xms8m
59171 Resin -Xss1m -XX:MaxGCPauseMillis=200 -Dnetworkaddress.cache.ttl=600 -Xms1001m -Xmx1001m -Dresin.server=1 -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djava.system.class.loader=com.caucho.loader.SystemClassLoader -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl -Djava.awt.headless=true -Dresin.home=/usr/local/resin/ -Dresin.watchdog=a -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl -Djava.awt.headless=true -Dresin.home=/usr/local/resin/ -Dresin.root=/usr/local/resin -Dresin.watchdog=a -Djava.util.logging.manager=com.caucho.log.LogManagerImpl -Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl -Djava.awt.headless=true -Dresin.home=/usr/local/resin/ -Dresin.root=/usr/local/resin
1
root@ip-10-21-151-113:~# jps -l
59117 com.caucho.boot.WatchdogManager
59171 com.caucho.server.resin.Resin
15486 sun.tools.jps.Jps

jstat命令

介绍:对堆的使用情况进行实时的命令行统计。

  • 类加载及卸载情况
  • 新生代,老年代及永久代的容量及使用情况
  • 新生代,老年代及永久代垃圾回收情况,垃圾回收次数和垃圾回收时间
  • 新生代中Eden区及Survior区中容量及分配情况等

使用格式

1
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

常用的option

1
root@ip-10-21-151-113:~# jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation

使用:

1
root@ip-10-21-151-113:~# jstat -gcutil 59171 1000 5
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
 57.29   0.00  63.16  79.74  81.34   1760  151.129     5    3.705  154.834
 57.29   0.00  63.16  79.74  81.34   1760  151.129     5    3.705  154.834
 57.29   0.00  63.16  79.74  81.34   1760  151.129     5    3.705  154.834
 57.29   0.00  63.16  79.74  81.34   1760  151.129     5    3.705  154.834
 57.29   0.00  64.39  79.74  81.34   1760  151.129     5    3.705  154.834
1
root@ip-10-21-151-113:~# jstat -gc 59171 1000 5
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
3072.0 3072.0 1760.1  0.0   335360.0 251031.8  683520.0   545011.9  83968.0 68301.5   1760  151.129   5      3.705  154.834
3072.0 3072.0 1760.1  0.0   335360.0 251153.7  683520.0   545011.9  83968.0 68301.5   1760  151.129   5      3.705  154.834
3072.0 3072.0 1760.1  0.0   335360.0 251153.7  683520.0   545011.9  83968.0 68301.5   1760  151.129   5      3.705  154.834
3072.0 3072.0 1760.1  0.0   335360.0 251153.7  683520.0   545011.9  83968.0 68301.5   1760  151.129   5      3.705  154.834
3072.0 3072.0 1760.1  0.0   335360.0 251153.7  683520.0   545011.9  83968.0 68301.5   1760  151.129   5      3.705  154.834

jmap命令

介绍:打印进程内存中的所有对象(对象类名,数量,大小)。

用法:

1
jmap [option] <pid>

常用参数:

1
-heap                to print java heap summary
-histo[:live]        to print histogram of java object heap; if the "live"
                     suboption is specified, only count live objects
-permstat            to print permanent generation statistics
-finalizerinfo       to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
                     dump-options:
                       live         dump only live objects; if not specified,
                                    all objects in the heap are dumped.
                       format=b     binary format
                       file=<file>  dump heap to <file>
                     Example: jmap -dump:live,format=b,file=heap.bin <pid>
-F                   force. Use with -dump:<dump-options> <pid> or -histo
                     to force a heap dump or histogram when <pid> does not
                     respond. The "live" suboption is not supported
                     in this mode.

示例:

打印所以对象的信息

1
root@ip-10-21-151-113:~# jmap -F  -histo 59117
Attaching to process ID 59117, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.45-b08
Iterating over heap. This may take a while...
Object Histogram:

num       #instances    #bytes  Class description
--------------------------------------------------------------------------
1:              9272    7797920 byte[]
2:              33781   4515096 * ConstMethodKlass
3:              33781   4333168 * MethodKlass
4:              2751    3170088 * ConstantPoolKlass
5:              2751    1971192 * InstanceKlassKlass
6:              2046    1726176 * ConstantPoolCacheKlass
7:              21264   1535528 char[]
8:              41      1349264 com.caucho.util.LruCache$CacheItem[]
9:              46838   749408  java.lang.Object
10:             1371    572952  int[]
11:             5682    473872  java.lang.Object[]
12:             19461   467064  java.lang.String
13:             2948    352512  java.lang.Class
14:             3478    278240  java.lang.reflect.Method
15:             613     254928  * MethodDataKlass
16:             4383    247776  * System ObjArray
17:             3670    223024  short[]
18:             6732    215424  java.util.HashMap$Entry
19:             5151    164832  java.util.concurrent.ConcurrentHashMap$HashEntry
20:             1238    146688  java.util.HashMap$Entry[]
21:             3479    139160  java.util.LinkedHashMap$Entry
22:             5504    132096  java.util.ArrayList

将内存dump出来(然后可以用MAT图形化工具来分析)

1
root@ip-10-21-151-15:~# jmap -dump:format=b,file=test.bin 51419
Dumping heap to /root/test.bin ...
Heap dump file created

root@ip-10-21-151-15:~# ll test.bin 
-rw------- 1 root root 25598311 Aug  2 16:15 test.bin

jstack打印java进程的堆栈信息

介绍:打印java进程的堆栈信息

使用:

1
jstack -F [-m] [-l] <pid>

相关参数

1
-F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m  to print both java and native frames (mixed mode)
-l  long listing. Prints additional information about locks

示例:

1
root@ip-10-21-151-15:~# jstack -F 51419
Attaching to process ID 51419, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.45-b08
Deadlock Detection:

No deadlocks found.

Thread 30624: (state = BLOCKED)
 - sun.misc.Unsafe.park(boolean, long) @bci=0 (Compiled frame; information may be imprecise)
 - java.util.concurrent.locks.LockSupport.park() @bci=5, line=315 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.nextTask() @bci=254, line=962 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.runTasks() @bci=26, line=887 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.run() @bci=69, line=866 (Interpreted frame)


Thread 56062: (state = BLOCKED)
 - sun.misc.Unsafe.park(boolean, long) @bci=0 (Compiled frame; information may be imprecise)
 - java.util.concurrent.locks.LockSupport.park() @bci=5, line=315 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.nextTask() @bci=254, line=962 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.runTasks() @bci=26, line=887 (Compiled frame)
 - com.caucho.util.ThreadPool$PoolThread.run() @bci=69, line=866 (Interpreted frame)

图形化工具MAT(memory analyze tool)

基本用法:

  • 先用jmap命令将内存dump出到一个文件中
  • 然后打开MAT,在MAT中打开dump文件
  • 然后进行相关分析

总结

jvm故障排查基本就用到这些工具,因为我们的服务器都是linux,都是命令行终端的,所以一般不会使用到windows下面的jdk自带的图形化工具。

熟练使用这些工具,可以使我们在故障排查迅速地找到问题的所在。

Java类加载过程与类加载器

目录
  1. ✔java类加载的过程
  2. ✔类加载器
    1. ✔双亲委派模型

java类加载的过程

image

  • 加载:java虚拟机加载二进制字节码流,产生的class对象存放在方法区中。
  • 验证:是否符合java虚拟机规范,主要验证:文件格式验证,元数据验证,字节码验证,符号引用验证。
  • 准备:将class对象的类变量(static修饰的基本类型(int,short,boolean等))赋值为零值(0,0,false),静态引用不在这个阶段赋值,在初始化阶段赋值(静态引用的对象在堆上面),如果是静态常量则赋值为ConstantValue(常量的值)
  • 解析:符号引用(符号标识)替换成直接引用(内存指向,指向目标的值)。
  • 初始化:执行类构造器()方法的过程:静态变量赋值动作和静态语句块(static{})的动作
  • 使用:实例化对象,方法调用等
  • 卸载:JVM回收类对象

类加载都是在运行时进行的,java反射机制其实就是用到类加载的过程。

类加载器

JVM里面一共有下面这几种类加载器:Boostrap ClassLoader(C++写的ClassLoader,加载JAVA_HOME/lib的类),Extention ClassLoader(加载jre\lib\ext下面的类),Application ClassLoader(用户自己定义的类)以及用户自己定义的ClassLoader. image

双亲委派模型

  • 类的唯一性是有类本身和类的类加载器确定的
  • 类加载的时候,都会尝试交给顶层的类加载器去加载,如果加载不了才会丢给下层的类加载器加载

记虎牙视频弹幕JVM故障排查与解决

目录
  1. ✔背景
  2. ✔排查
  3. ✔解决
  4. ✔教训

背景

3月份在做一个虎牙视频弹幕的项目,完成设计和开发后进行功能测试没有什么问题,因为这个项目弹幕的获取和新增都是去调用虎牙那边的thrift服务,压力主要还是在他们那边,加之产品经理着急项目赶紧上线,所有没有进行压力测试,功能测试OK了就赶紧发布了。

项目上线进行一些功能回归测试是没有问题的,一段时间后,测试同学和产品发现线上的弹幕列表没办法获取,新增弹幕也失败。

排查

我赶紧打开浏览器请求一下相关接口,没想到直接超时了。

然后进去服务器,看了一下,并没有明显的报错日志;我在想会不会是java经常crash了,用了

1
ps -ef|grep java

查看了一下,java进程(tomcat)是在的,那为什么接口访问直接超时呢,是不是java进程已经假死了。

我赶紧用jstat看一下java进程的GC情况:

1
jstat -gcutil pid

看了一下,进程基本没有GC发生,Perm space(永久代)占了95%+。

开始,我觉得是不是老年代设得太小导致了,full GC频繁导致的???那为什么不会出现OOME,我把内存都调大了一倍左右,Permentent Space也增加了一倍左右。

然后用ab进行压力测试,发现刚开始就是Young GC比较频繁(这个正常,因为正在压力测试,young GC频繁正常),Full GC并不是非常频繁,Permentent Space 也一直稳定在一半大小以下(因为比之前调大了一倍啦)。

那这TM就奇怪了。。。

。。。。。。

我还是不死心一直怀疑是java进程的内存设置有问题,然后我又用jmap -histo 打印每个class的实例数目,内存占用,类全名信息。

1
jmap -histo pid

看了一下,也没有那个类的数目非常地多,或者占用了非常多的内存。

。。。

从JVM的内存情况实在TMD看不出个所以然,那就只能看一下线程堆栈了,赶紧把线程堆栈打印出来看看。

1
jstack -f pid

终于从线程堆栈可以看出点端倪来。

image

我们可以看出

1
getDanmuList()

这个获取弹幕列表方法阻塞了,接着看一下堆栈里面调用了多少次这个方法:

image

我们可以看出这个方法调用了150次之多,而且这150条线程都处于阻塞状态,这么多次阻塞,直接导致整个web服务器进程假死了。。。

看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public List<DanmuVO> getDanmuList(long vid) {
ThriftClientWrapper<VideoMessageServant.Iface> client = null;
List<DanmuVO> list = new ArrayList<DanmuVO>();
try {
// 取得客户端
client = danmuFactory.createClient();

// 业务逻辑
// ...

} catch (Exception e) {
LOG.error(String.format("获取视频%s弹幕列表失败", vid), e);
}

return list;
}

逻辑主要是获取一个thrift的连接,然后去调用服务化的接口,我们可以清楚地看到代码的最后并没有关闭连接。

因为每次调用服务化接口,需要打开一个连接,但是调用完了并没有关闭,调用一多,连接池就没有连接可用了,后面调用服务化接口的请求就一直在等待,最后导致大量线程阻塞了,直接造成整个tomcat假死了。

解决

代码加上关闭连接的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public List<DanmuVO> getDanmuList(long vid) {
ThriftClientWrapper<VideoMessageServant.Iface> client = null;
List<DanmuVO> list = new ArrayList<DanmuVO>();
try {
// 取得客户端
client = danmuFactory.createClient();
// 业务逻辑
// ...

} catch (Exception e) {
LOG.error(String.format("获取视频%s弹幕列表失败", vid), e);
}finally {
// 关闭连接
if(client != null){
client.close();
}
}

return list;
}

然后在用ab压测了一下,然后在看一下线程堆栈情况,终于恢复正常了。

教训

  • 无论项目使用人数多少,都要进行压力测试。
  • 应用有外部系统调用,要记得有相关的连接关闭操作。
  • 应用有IO操作,也要有相关的IO关闭操作。

JAVA集合(二)-并发集合

目录
  1. ✔ConcurrentHashMap
  2. ✔CopyOnWriteList
  3. ✔未完待续。。。。。

ConcurrentHashMap

  • 并发的HashMap,避免HashMap在并发环境下产生死循环
  • 内部默认由16个Segment组成,每个segment内部有一个Entity数组,用来存放元素 image
  • put操作会对写入元素的进行hash值的计算,由hash值定位其所在的segment,然后对segment进行上锁再进行写操作
  • get操作则不会上锁,由于count,value都定义为volatile(线程可见),volatile字段的写入操作先于读操作,所以拿到的都是最新的.

CopyOnWriteList

  • 写操作的时候会copy原来的数组,进行写操作,然后改引用的的指向
  • 只能做到数据最终一致性,不能做到强一致性
  • 用于读多写少的场景

未完待续。。。。。

Java集合(一)

目录
  1. 普通集合
    1. ✔List
      1. ✔ArrayList
      2. ✔LinkedList
    2. ✔Map
      1. ✔HashMap
      2. ✔TreeMap
    3. ✔Set
      1. ✔HashSet
    4. ✔完

普通集合

List

ArrayList

特性:

  • 内部是一个数组,初始状态容易为10, 数组满了之后扩容(数组复制), 为capacity+capacity>>1=1.5*capacity

基本方法:

  • add(E):O(1),内部数组扩容的话会是O(N)
  • add(int,E):O(N)
  • get(int):O(N)
  • remove(int):O(N),会移动数组
  • remove(Object):O(N),会移动数组

使用:

  • 尽量在初始化时就指定容量,这样有助于减小数组扩容带来的内存和CPU的损耗

LinkedList

特性:

  • 一个双向链表,每个元素都有两个指向,一个指向前一个元素,一个指向后一个元素
  • 读取某个位置的元素,需要进行遍历;添加元素到列表的头或尾会比较快O(1)

基本方法:

  • addFirst(E),addLast(E),add(E):O(N)
  • getFirst(),getLast():O(1)
  • get(int):需要遍历,时间复杂度为O(N)
  • removeFirst(),removeLast(),remove():O(1)
  • remove(E),remove(int):O(N)

Map

HashMap

特性:

  • 用数组来做哈希表,初始大小为16
  • 发生hash冲突时,会以列表的形式存储,当冲突超过8个,会以红黑树来存储(jdk1.8新属性)
  • 如果HashMap的大小超过负载因子(默认是3/4),就会resize为原来的2倍
  • resize的时候,bucket<<1(原来的2倍),冲突的元素要么在原来的位置,要么是原来移动2次幂( 减少重新计算hash值

使用:

  • 尽量在初始化时就指定容量
  • 并发环境千万不要用HashMap,会出现死循环,请用并发集合ConcurrentHashMap

TreeMap

特性:

  • 红黑树实现,是一种自平衡二叉树
  • 查询,插入都是LOG(N)

Set

HashSet

特性:

  • 其实就是集成了一个HashMap,key来存东西,value都设置为null

2017年——关键的一年

目录
  1. ✔身体
    1. ✔饮食
    2. ✔运动
    3. ✔作息
  2. ✔学习(形成文档记录)
    1. ✔第一阶段(4月-5月):基础
    2. ✔第二阶段(5月-6月):数据层
    3. ✔第三阶段(6月-7月): 架构与中间件
    4. ✔第三阶段(7-8月):复习
    5. ✔后面,待续…
  3. ✔工作
  4. ✔后话

本来是大略的写过今年要做的事,但是还是不够细致,没具体说到要达到什么样的效果。 还有的就是要具体分析一下今年的情况,做最好的选择,无论是身体,学习,工作。

身体

饮食

  1. 多吃水果
  2. 不喝饮料
  3. 不吃垃圾食品:油炸食品,泡面
  4. 少吃炒的食物
  5. 不吃过咸,油腻的食物
  6. 晚上过了九点不要吃东西
  7. 不要暴饮暴食,吃东西慢一点再慢一点,没人和你抢

运动

  1. 坚持每周都有运动
  2. 跑步三次
  3. 健身2次
  4. 晚上夜跑最适合了
  5. 早上可以做一下无氧运动

作息

  1. 坚持每天12点前睡觉
  2. 坚持每天8点前起床
  3. 睡觉前少玩手机
  4. 起床前也少玩手机

学习(形成文档记录)

第一阶段(4月-5月):基础

  1. java基础:面向对象,JVM,集合,IO,反射等
  2. spring,mybatis框架

第二阶段(5月-6月):数据层

  1. 数据库架构,索引优化等
  2. redis的具体操作与基本的架构

第三阶段(6月-7月): 架构与中间件

  1. 负载均衡
  2. 数据库连接池
  3. MQ
  4. SOA等

第三阶段(7-8月):复习

  1. 所有的复习一遍
  2. 选择一个东西重点深入研究

后面,待续…

工作

  1. 对于需求,先分步设计好怎么搞,然后一步一步实现
  2. 对于影视组这个项目,估计搞不久,后面要找个时间把整个项目捋一遍
  3. 今年估计就不跳槽了,但是但是一定要去试一下,看看外面的情况
  4. 随机应变,如果这边玩不了,最好就去K12那边
  5. 不要被需求压死,不要太傻

后话

最好希望自己能够逐渐安静下来,平静下来,calm down!!!😄😄😄