大部分内容都是《深入理解Java虚拟机上的内容》的总结,少部分内容是来自于网上或者自己的理解。读完应该会把没笔记的markdown文件放在github上。

本部分笔记对应的是《深入理解Java虚拟机》第四章,第五章和第六章。

虚拟机性能监控和故障处理工具

命令行工具

名称

主要作用

jps

JVM Process Status Tool,显示指定系统内所有HotSpot虚拟机进程

jstat

JVM Statistics Monitoring Tool,用于收集HotSpot虚拟机个方面运行数据

jinfo

Configuration Info for Java,显示虚拟机配置信息

jmap

Memory Map for Java,生成虚拟机的内存转储快照(heapdump文件)

jhat

JVM Heap Dump Browser,用于分析heapdump文件,它会建立一个HTTP/HTML服务器让用户可以在浏览器上查看分析结果

jstack

Stack Trace for Java,显示虚拟机的线程快照

可视化工具

  • JConsole
  • VisualVM

调优案例分析与实战

通过64位JDK来使用内存

  • 内存回收导致长时间的停顿
  • 相同程序在64位JDK消耗的内存一般比32位的大,由于指针膨胀,数据类型对齐等原因

使用若干个32位虚拟机建立逻辑集群来利用硬件资源

  • 节点的全局竞争,磁盘竞争
  • 很难高效的利用某些资源池
  • 受到32位的内存限制
  • 大量的使用本地缓存

垃圾回收时,虚拟机虽然会对Direct Memory进行回收,但是Direct Memory却不能向新生代,老年代那样,发现空间不足就通知收集器进行垃圾回收,它只能等待老年代满了后发生Full GC,然后帮它清理掉内存的废弃对象,否者它只能抛出内存溢出异常。

类文件结构

Class文件结构

各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)

实现语言无关性的基础是虚拟机和字节码存储格式,Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件有所关联,Class文件中包括了Java虚拟机指令集和符号表以及若干其他辅助信息。

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,当遇到占用8字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储中间没有分隔符,所以Class文件内容都是被严格限定的

Class文件中结构只有两个类型--无符号和表

无符号

基本的数据类型,u1,u2,u4,u8表示1个字节,2个字节,4个字节,8个字节的无符号数

由多个无符号数或者其他表作为数据结构项构成复合数据数据类型,表都是习惯性以_info结尾。整个Class文件本质上就是一张表

类型 名称 数量 说明
u4 magic 1 魔术 0xCAFEBABE
u2 minor_version 1 次版本号
u2 major_version 1 主版本号
u2 constant_pool_count 1 常量池容量计数值
cp_info constant_pool constant_pool_count - 1
u2 access_flags 1 访问标志
u2 this_class 1 类索引
u2 super_class 1 父类索引
u2 interfaces_count 1
u2 interfaces interfaces_count 接口索引集合
u2 fields_count 1
field_info fields fields_count 字段表集合
u2 methods_count 1
method_info methods methods_count 方法表集合
u2 attributes_count 1
attribute_info attributes attributes_count 属性表集合

字节码指令

Java虚拟机的指令由一个字节长度的,代表着某种特定操作含义的数据(称为操作码,Opcode)以及跟随后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。类似于机器指令,在属性表集合中的Code属性中。Java的操作码只有1个字节长度。

操作码助记符

i代表int类型数据操作,l代表long,s代表short,b代表byte,c代表char,d代表double,f代表float,a代表reference。**

加载和存储指令

  • 将一个局部变量加载到操作栈 <操作码助记符>load。
  • 将一个数值从操作栈存储到局部变量表 <操作码助记符>store
  • 将一个常量加载到操作栈,bipush,sipush,ldc,ldc_w, ldc2_w, aconst_null, iconen_m1, iconst__, lconst_<l>, fconst__<f>, dconst__<d>,
  • 扩充局部变量表的访问索引指令,wide
  • 把一个数组元素加载到操作数栈的指令,<操作码助记符>aload
  • 将一个操作数栈的值存储到数组元素中的指令,<操作码助记符>astore

运算指令符

  • 加法指令,<操作码助记符>add
  • 减法指令,<操作码助记符>sub
  • 乘法指令,<操作码助记符>mul
  • 除法指令,<操作码助记符>div
  • 求余指令,<操作码助记符>rem
  • 取反指令,<操作码助记符>neg
  • 位移指令,ishl, ishr, iushr, lshl, lshr, lushr
  • 按位或指令,ior, lor
  • 按位与指令,iand, land
  • 按位异或指令,ixor, lxor
  • 局部变量自增指令,iinc
  • 比较指令,dcmpg, dcmpl, fcmpg, fcmpl, lcmp

非正规浮点数值, 逐级下溢

类型转换指令

i2b, i2c, i2s,

l2i,

f2i,f2l,

d2i,d2l,d2f

对象创建与访问指令

  • 创建类实例的指令 new
  • 创建数组的指令 newarray,anewarray,multianewarray
  • 访问类字段和实例字段,getfield,putfield,getstatic,putstatic
  • 获取数组长度的指令,arraylength
  • 检查类实例类型的指令,instanceof,checkcast

操作数栈管理指令

  • 将操作数的栈顶一个或者儿歌元素出栈,pop,pop2
  • 复制栈顶一个或两个数值并将复制值或双份复制值重新压入栈顶,dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2
  • 将栈最顶端的两个数值互换 swap

控制转移指令

  • 条件分支, ifeq, iflt, ifle, ifne, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmplt, if_icmpgt, if_icmple,if_icmpge, if_acmpeq 和if_acmpne
  • 复合条件分支, tableswitch,lookupswitch
  • 无条件分支,goto,goto_w,jsr,jsr_w,ret

方法调用和返回指令

方法调用

  • invokevirtual,调用对象的实例方法
  • invokeinterface, 调用接口方法,它会在运行时,搜索一个实现了这个接口方法的对象
  • invokespecial,调用一些需要特殊处理的实方法,如实例初始化方法,实例私有方法或父类方法
  • invokestatic,调用类方法
  • invokedynamic,在运行时动态解析出调用点限定符所引用的方法

返回指令

  • 返回null,return
  • 返回类型,<操作码助记符>return

异常处理指令

在java程序中都是有athrow指令实现。在java虚拟机中有异常表实现

同步指令

Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这个两种同步结构都是使用管程实现

方法级的同步,是隐式的。通过方法表中的ACC_SYNCHRONIZED访问标志来申明是否为同步方法。如果是,执行线程要求先成功持有管程,然后在执行方法,执行完之后释放管程。

同步一段指令序列通常由synchronize语句块,由monitorenter和monitorexit两条指令来支持synchronize关键字的语义。


fish
101 声望2 粉丝

希望你能够学习新的技术