JVM由三个主要的子系统构成
- 类加载器子系统
- 运行时数据区(内存结构)
- 执行引擎
下图来源于一位大牛之手:
1.1 类加载系统
类加载过程如下
- 类加载:类加载器将class文件加载到虚拟机的内存
- 加载:在硬盘上查找并通过IO读入字节码文件
- 连接:执行校验、准备、解析(可选)步骤
- 校验:校验字节码文件的正确性
- 准备:给类的静态变量分配内存,并赋予默认值
- 解析:类装载器装入类所引用的其他所有类
- 初始化:对类的静态变量初始化为指定的值,执行静态代码块
类加载器类别:
- 启动类加载器:负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等
- 扩展类加载器:负责加载JRE扩展目录ext中JAR类包
- 系统类加载器:负责加载ClassPath路径下的类包
- 用户自定义加载器:负责加载用户自定义路径下的类包
类加载通过双亲委派模式和全盘负责委托机制
- 全盘负责委托机制:当一个ClassLoader加载一个类时,除非显示的使用另一个ClassLoader,该类所依赖和引用的类也由这个ClassLoader载入
- 双亲委派机制:指先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类
1.2 JVM内存分析
- jinfo查看jvm或java系统参数,常用命令有:(pid为java进程ID)
jinfo -flags pid 查看jvm的参数
jinfo -sysprops pid 查看java信息
- jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。命令的格式如下;
jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]
如: jstat -class pid 统计jvm加载类信息
jstat -gc pid 垃圾回收统计
jstat -gccapacity pid 堆内存统计
jstat -gcnew pid 新生代垃圾回收统计
jstat -gcnewcapacity 新生代内存统计
jstat -gcold pid 老年代垃圾 回收统计
jstat -gcoldcapacity pid 老年代内存统计
jstat -gcmetacapacity pid 元空间统计
jstat -gcutil pid垃圾回收消耗总耗时
- jmap用来查看内存信息
jmap -histo pid 查看实例个数及占用内存大小
jmap -heap pid 查看堆信息
jmap -dump:format=b,file=eureka.hprof pid 导出内存信息
- jstack查看jvm线程信息,可以用于查找死锁
jstack pid
用jstack找出占用cpu最高的堆栈信息
- 使用命令top -p <pid> ,显示你的java进程的内存情况,pid是你的java进程号,比如4977
- 按H,获取每个线程的内存情况
- 找到内存和cpu占用最高的线程tid,比如4977
- 转为十六进制得到 0x1371 ,此为线程id的十六进制表示
- 执行 jstack 4977|grep -A 10 1371,得到线程堆栈信息中1371这个线程所在行的后面10行
- 查看对应的堆栈信息找出可能存在问题的代码