由于class文件属于jvm的核心基础,涉及到很多方面,这里只是梳理class文件的关键骨架
类型 | 项目 | 说明 |
---|---|---|
u4 | magic | 文件格式标志,固定为0xCAFEBABE |
u2 | minor_version | 字节码文件次版本号 |
u2 | major_version | 字节码文件主版本号 |
u2 | constant_pool_count | 常量池的大小 |
cp_info | constant_pool_count | 常量池 |
u2 | access_flags | 访问修饰符,如public、private等 |
u2 | this_class | class文件定义的类或接口,这里是对应常量池的索引 |
u2 | super_class | 父类,对应常量池的索引 |
u2 | interfaces_count | 接口数量 |
u2 | interfaces[interfaces_count] | 接口数组,此处是常量池的索引 |
u2 | fields_count | 字段数量 |
field_info | fields[fields_count] | field_info类型数组 |
u2 | methods_count | 方法数量 |
method_info | methods[methods_count] | method_info类型数组 |
u2 | attributes_count | 属性数量 |
attribute_info | attributes[attributes_count] | attribute_info类型数组 |
类型 | 项目 | 说明 |
---|---|---|
u1 | tag | 常量类型,截止java se 16,常量类型有17类 |
u1 | info[] | 该项的内容依据tag变化 |
类型 | 项目 | 说明 |
---|---|---|
u2 | access_flags | 访问修饰符 |
u2 | name_index | 方法名,常量池索引 |
u2 | descriptor_index | 方法描述,常量池索引 |
u2 | attributes_count | 属性数量 |
attribute_info | attributes[attributes_count] | 属性信息数组 |
类型 | 项目 | 说明 |
---|---|---|
u2 | access_flags | 访问修饰符 |
u2 | name_index | 方法名,常量池索引 |
u2 | descriptor_index | 方法描述,常量池索引 |
u2 | attributes_count | 属性数量 |
attribute_info | attributes[attributes_count] | 属性信息数组 |
类型 | 项目 | 说明 |
---|---|---|
u2 | attribute_name_index | 属性名,常量池索引 |
u4 | attribute_length | info数组长度 |
u1 | info[attribute_length] | 属性信息数组 |
其中属性分为三大类:
JVM指令(SE 16)目前分为9大类,具体每个指令的详细说明请参考JVM规范。
总体来说,JVM运行时内存可分为静态信息和动态信息两部分,静态信息即为类(接口)的结构信息,动态信息即为类的实例数据和代码执行信息
对于对象的结构,jvm规范没有要求,这里主要介绍hotspot的对象内存结构
普通对象包括mark word,以及实例的类型信息class word和实例字段,mark word 和class word 称为对象头
对于数组对象,除了mark word和class word外,还包括数组的长度以及数组元素
mark word在32位环境下位4个字节,64位环境下为8字节,32位环境下mark word不同状态下的结构信息如下:
64位环境下的mark word不同状态下的结构信息如下:
mark word主要包括三方面的信息:
class word主要指向实例的类信息,主要作用包括运行时类型检查、对象大小计算以及接口或虚拟方法调用
当对象大小不是8的倍数,jvm会采取填充的方式使对象的大小刚好为8的倍数,这样做是为了保持数据的一致,便于快速的数据存取,为了清楚的了解对象的结构,这里我们使用JOL工具,具体使用参见JOL项目主页
java -jar jol-cli.jar internals java.lang.Object
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00
4 4 (object header) 00 00 00 00
8 4 (object header) a8 0e 00 00
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
由于Object没有字段,所以在对象头后面有4字节的填充,下面我们看下Integer:
java -jar jol-cli.jar internals java.lang.Integer
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
Instantiated the sample instance via public java.lang.Integer(int)
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00
4 4 (object header) 00 00 00 00
8 4 (object header) f0 0e 01 00
12 4 int Integer.value 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
可以看见,对象头后面的4个字节被value字段占用。
public class ObjectAlignment{
public Long l;
public int i;
public int i2;
public boolean b;
}
ObjectAlignment object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int ObjectAlignment.i N/A
16 4 int ObjectAlignment.i2 N/A
20 1 boolean ObjectAlignment.b N/A
21 3 (alignment/padding gap)
24 4 java.lang.Long ObjectAlignment.l N/A
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
public static class A{
long a;
}
public static class B extends A{
int b;
}
public static class C extends B{
int c;
}
ObjectAlignment$C object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x2000c1c0
12 4 (alignment/padding gap)
16 8 long A.a 0
24 4 int B.b 0
28 4 int C.c 0
Instance size: 32 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
public static class A{
long a;
}
public static class B extends A{
int b;
boolean b2;
}
public static class C extends B{
boolean c2;
}
ObjectAlignment$C object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x2000c1c0
12 4 (alignment/padding gap)
16 8 long A.a 0
24 4 int B.b 0
28 1 boolean B.b2 false
29 3 (alignment/padding gap)
32 1 boolean C.c2 false
33 7 (object alignment gap)
Instance size: 40 bytes
Space losses: 7 bytes internal + 7 bytes external = 14 bytes total
以上规则实验环境为jdk8,不同的jdk版本实现细节有所差异
所有的用户定义加载器从类的继承关系上来说,都是ClassLoader的子类
当加载一个类时,首先定义该类的类加载器查找是否已经加载,如果未被加载,则委托其父加载器(类加载器parent字段指定的加载器,而不是继承关系的父加载器),直到bootstrap,如果bootstrap中未找到,bootstrap则委托给其子加载器,直到定义该类的加载器
Linking阶段分为Verification、Preparation、Resolution三个阶段
Verification主要验证文件的结构正确
Preparation主要是创建类或结构的静态字段,并初始化默认值,不涉及任何jvm代码执行
Resolution解析符号引用为对运行时常量池的直接引用
执行类或接口的初始化方法(clinit),即静态初始化方法
为了提高效率,优化程序,编译器、处理器、运行时系统以及多层次存储系统会对程序执行的指令顺序、数据的读写顺序及时间进行重排序,导致与预想的结果不一致,内存模型在不使用同步语句的情况下,为原子性、可见性和有序性提供了最小保证
存取非long/double字段都是原子操作,由于long/double字段涉及高低位,所以需要加上volatile,原子性能保证并发情况下字段的数据位不出现混乱,但是不能保证获得最新的值
一个线程对字段值做了修改,只有在以下情况下才能被其他线程所见:
单GC线程,采用copy算法的年轻代收集器,应用要停顿STW(stop the world)
多GC线程并发、采用copy算法的年轻代收集器,应用要停顿STW(stop the world),可以与CMS配合使用,以减少停顿为目标
多GC线程并发、采用copy算法的年轻代收集器,应用要停顿STW(stop the world),以提高吞吐量为目标
单GC线程,采用标记压缩算法的老年代收集器,应用要停顿STW(stop the world)
多GC线程,采用标记压缩算法的老年代收集器,应用要停顿STW(stop the world)
CMS(ConcurrentMarkSweep)里程牌式的老年代并发回收器,诞生1.4之后,开启了并发回收时代,垃圾回收和应用运行同时进行,降低了停顿时间(STW),由于采用MarkSweep算法,必然产生碎片,如果空间不足,将使用Serial Old进行垃圾回收,STW时间与老年代大小成正比,由于CMS问题多,所以所有的JDK版本默认GC都不是CMS
并发标记清除,主要分为4个阶段:
G1把堆划分为大小相同的多个区域,每个区域要么为空,要么被分配为年轻代或老年代,内存分配时,内存管理分发一个空闲区域,然后指定一个代,有应用程序在空闲空间内自己分配
白色:尚未被标记
灰色:对象本身已被标记,但是引用到的对象未被标记
黑色:对象本身和引用到的对象都已被标记
由于在标记过程中,应用也在运行,因此可能产生多标和漏标
A已经被标记完,此刻,切断A对B的引用,导致A不会被重新标记,而B被认为是存活的,仍然 被遍历,此时BCD应该被回收,但仍然存活,只能下次被回收,但不影响程序的正确性
上面的漏标转换成代码即如下:
D d = b.d;
b.d = null;
a.d = d;
我们只要对这三步中的任一步进行拦截,把变化记录下来进行重标就可以,其主要有两种方式:
待更新
待更新
确定目标:根据业务场景确定目标,响应时间优先还是吞吐量优先,比如数据挖掘、科学计算吐出量优先,网站响应时间优先
选定回收器组合
确定内存大小及CPU
设定分代(区)大小和升级老年代年龄
设置日志参数
-Xloggc:/xxx/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
观察分析日志做进一步优化
arthas