深入理解JVM(一)

335 阅读3分钟

JVM定义

跨语言的平台

字节码文件的运行环境

  • 只要能够依据JVM规划(JVM Specification)编译成字节码文件(.class)的语言都可以在JVM环境上运行。
  • JVM本身与java没有绝对关系,而是针对于字节码文件,符合规范的.class文件都能够享受JVM带来的良好的运行环境。
  • JVM本身是一种规范

class file format

本质:二进制字节流

查看方式:

  • 查看十六进制格式的ClassFile
    • subline/notepad
    • IDEA插件-BinEd
  • 其他ByteCode的观察方式:
    • javap + 一个指令 + class文件地址
    • JBE(Java ByteCode Editor)-可以直接修改
    • JClassLib - IDEA插件之一 内容:
  • Magic Number
    • "CAFEBABE"
  • constant_pool_count:常量池数据量
  • constant_pool:常量池中数据
  • access_flags:修饰符public、final等信息
  • this_class
  • super_class
  • field_count
  • fields
  • interfaces_count
  • interfaces
  • methods_count
  • methods
  • attributes_count
  • attributes

class的可移植性

  • 只要能够编译成为.class的语言,都具备移植性,原因在于:只要当前内核环境下存在符合当前内核的JVM进程(比如:Windows有windows版本的JVM,MacOS有MacOS的JVM,Linux有...),有JVM存在,class文件都能够直接运行,JVM解决了与内核或硬件适配的问题。因此只要能够编译成.class的语言都具备“一次编译,到处运行的能力”,其主要依托于有JVM存在。

JVM版本实现

  • Hotspot:orcle官方开发,jdk使用的官方JVM;
  • Jrockit:BEA开发,被Orcle收购,合并入Hotspot;
  • J9:IBM
  • Microsoft VM
  • Taobao VM:Hotspot深度定制版
  • Liquid VM:直接针对硬件
  • azul zing:最新垃圾回收的标杆

JDK

Java从编译到执行

对象的创建

  1. 创建对象:

    1. 分配内存空间;
    2. 为实例变量赋初值;
    3. 设置对象头信息,包括对象的hashcode(只有对hashcode进行了调用才会存入进来),GC分代年龄,元数据信息;
    4. 执行构造器进行初始化;
    

对象在内存中的布局

普通对象

  • 对象头:(12字节

    • markword(8字节):关于锁状态、锁升级、hashcode、gc信息等;

    • class pointer(4字节或8字节):类型指针,获取Clazz对象的途径之一,对象类型的标识;

      • 默认32GB内存下,32位寻址能力足够,JVM开启compress class pointer,因此会压缩至4字节;
  • 实例数据:instance data

    • 类中成员变量,具体大小根据变量数量和类型决定;
    • 比如:一个int类型,那么就需要占4个字节;如果是long类型,就需要占8字节;
  • 对齐:padding

    • 保证对象所占字节数能够被8整除,从而读取的时候效率更高(cpu读取数据是根据总线的宽度来读取,保证是8字节的倍数,读取效率高);
    • 如果当前所占内存不是8的倍数,会自动通过padding进行空间的补齐;

指针压缩原因:

  • 32位对应能够记录2的32次方-1个不同的地址,约等于4G个数量级;

  • 每个java对象大小必然是超过8字节(markword已经超过8字节);

  • 每个对象只需要对应一个起始地址;

  • 综上:32GB内存下,32位寻址能力必然足够,可开启指针压缩,节省空间。

数组对象

相比普通对象,多一个属性length属性(4字节

JVM内存模型

image.png

线程共享

  • 堆区
    • 绝大部分对象完成生命周期的场所,GC的核心区域;
  • 方法区
    • 存放类信息、各种常量池、runtime constentpool data;

线程私有

  • 虚拟机栈
    • 普通方法的调用栈,每个方法对应一个栈帧,当前方法对应当前栈帧;
  • 本地方法栈
    • native method调用栈,当前方法对应当前栈帧;
  • 程序计数器
    • 记录所执行到的指令位置,线程重新获取CPU时间时,继续从此位置开始执行;