Java虚拟机那些事(2)
性能监控与故障处理工具
JDK命令行工具
jps:虚拟机进程状况工具
jstat:虚拟机统计信息监视工具
jinfo:java配置信息工具
jmap:用于生成堆转储快照
jhat:虚拟机堆转储快照分析工具
jstack:java堆栈跟踪工具
JDK可视化工具
jconsole
visualVM
类文件结构
1.魔数:这头四个字节唯一的作用是确定这个文件是否为一个能被虚拟机接受的文件。
2.次版本号和主版本号
3.常量池入口和容量计数器。常量池主要存放两大类常量:字面量和符号引用。
字面量:文本字符串,被声明为final的常量值。
符号引用(编译原理方面的概念):类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。
4.访问标记:用于识别一些类或接口层次的访问信息
5.类索引、父类索引与接口索引集合:确定这个类的继承关系
6.字段表集合
7.方法表集合
8.属性表集合
虚拟机类加载机制
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。
与那些在编译时需要进行连接工作的语言(如C++)不同,java语言中,类型的加载和连接过程是在程序运行时完成的,这会增加一些性能开销,但是带来了更高的灵活性。
类加载的过程
1.加载:通过类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储转换为方法区的运行时数据结构,在java堆中生成了一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
2.验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。文件格式验证,元数据验证(语义分析),字节码验证,符号引用验证。
3.准备:为类变量分配内存并设置类变量初始值,这些内存将在方法区进行分配。
4.解析:将常量池内的符号引用替换为直接引用的过程。类或接口的解析,字段解析,类方法解析,接口方法解析。
5.初始化:最后一步
类加载器
三种系统提供的类加载器:
1.启动类加载器(bootstrap classloader):加载JAVA_HOME/lib目录下的类库,无法被java程序使用。
2.扩展类加载器(extension classloader):加载JAVA_HOME/lib/ext目录下的类库。
3.应用程序类加载器(application classloader):也称为系统类加载器,它负载加载用户路径上所指定的类库。它是程序中默认的类加载器。
当然也可以自定义类加载器
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此。因此所有的加载请求最终会传达到顶层的启动类加载器中,只有加载器反馈自己无法完成这个加载请求时(找不到所需的类),子加载器才会尝试去加载。
原因:避免混乱。因为就算是同一个类,不同加载器加载的话,也会被认为是两个不同的类。
关于方法区的一些补充
其实,从Java7开始就对方法区进行了一些调整;到Java8, 方法区的概念已经没有了,取而代之的是元空间MetaSpace这个概念。
Java7
符号引用:被挪到了本地内存
常量池和类的静态变量:被挪到了java堆
Java8
类信息被挪到了本地内存区域,至次,方法区移除工作完成。
可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize来进行调整元空间的大小。当超过大小设置时,会对死亡的类加载器和类进行GC 。
每个类加载器存储区称为一个metaspace,会回收整个类加载器,减少碎片。