jstack分析线程状态
如何查看cpu中占用率较高的线程并分析原因
- 在linux终端,执行top,它默认是按照使用率排序的,所以查看第一行,获取进程id,假设id为123
- 执行top -Hp 123,查看该进程下各个线程的执行情况
- 执行jstack 123查看该线程下的堆栈使用情况,
- 执行jmap -dump:live 123,下载该进程下存活的对象
- 使用MAT工具 加载该文件,分析问题
简述
虚拟机把描述类的数据结构从Class文件中加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
![](https://img.haomeiwen.com/i3130295/537f18ccd493e2c0.png)
类的生命周期主要包括加载、验证、准备、解析、初始化、使用和卸载。其中,加载、验证、准备和初始化的顺序是确定的,但这种顺序不是按部就班的进行,通常这些阶段通常都是相互交叉的混合进行,通常会在一个阶段执行的过程中调用、激活另一个阶段。
加载
在此阶段主要做下面三件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
在此阶段的操作有很高的灵活性,比如上述的第一件事,开发人员可以从不同方面进行操作,
- 从zip包中读取,最终成为日后的JAR、EAR、WAR格式的基础。
- 从网络中获取
- 运行时计算生成
- 从数据库中读取 等
验证
目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求。
- 文件格式验证,验证字节流是否符合Class文件格式的规范。
- 元数据验证,对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求
- 字节码验证,通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
准备
该阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
该阶段不包括实例变量,实例对象将会在对象实例化时随着对象一起分配在Java堆中。
解析
该阶段是将常量池中的符号引用替换成直接引用。
常量池中主要存放两大类常量:字面量和符号引用。
字面量类似于Java语言层面的常量概念,如文本字符串、声明为final的常量。
符号引用则属于编译原理方面的概念,主要包括下面三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
在Class文件中不会保存各个方法、字段的最终布局信息,需要从常量池中获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中。
初始化
初始化是为类的静态变量赋予正确的初始值,准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10
类加载器
把类加载阶段的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作交给虚拟机之外的类加载器来完成。这样的好处在于,我们可以自行实现类加载器来加载其他格式的类,只要是二进制字节流就行,这就大大增强了加载器灵活性。系统自带的类加载器分为三种:
- 启动类加载器。
- 扩展类加载器
- 应用程序类加载器。
双亲委派模型
如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是,依次向上递归请求,访问到最顶端时,然后最顶端类判断能否完成这个加载请求,如果该类能执行的话,就执行然后跳出程序,下面的类无需执行;不能执行的话,则再跳转到相邻的下一个子类,依次类推。
网友评论