执行Class文件的环境

虚拟机结构

虚拟机是执行Class文件的环境,通过类加载子系统加载进来,其一般包括:运行时数据区域、执行引擎(即时编译器、垃圾回收器)、本地库接口、本地方法库。

Class文件格式

此二进制格式文件独立于特定的硬件与操作系统。

任何语言,只要能被编译成Class文件,就可以被Java虚拟机识别并执行。

每一个Class文件中都对应着唯一的类或者接口的定义信息。包含的信息有:

  • 副、主版本号
  • 常量池计数器、常量池
  • 类和接口层次的访问标志
  • 接口计数器、接口表
  • 字段计数器、字段表
  • 方法计数器、方法表
  • 属性计数器、属性表

总之,通过已定规则,将类或接口的信息转换成Class文件格式。

类的生命周期

一般指:Java文件被加载到Java虚拟机内存中到从内存中卸载的过程。

大致如下:

  • 加载:查找并加载Class文件
  • 链接:包括验证、准备和解析
    验证:确保被导入类型的正确性
    准备:为类的静态字段分配字段,并用默认值初始化这些字段
    解析:虚拟机将常量池内的符号引用替换为直接引用
  • 初始化:将类变量出事为正确初始值

类加载子系统

有2种类加载器:系统加载器和自定义加载器。其中,系统加载器包括下面3种:

  • Bootstrap ClassLoader(引导类加载器)
    C/C++实现,加载指定的JDK的核心类库
  • Extensions ClassLoader(拓展类加载器)
    用于加载Java的拓展类
  • Application ClassLoader(应用程序类加载器)

运行时数据区域

  • 方法区
    线程共有
  • Java堆
    线程共有
  • Java虚拟机栈
    线程私有,有OOM,保存Java程序的栈帧
  • 本地方法栈
    线程私有,有OOM,保存Native程序的栈帧
  • 程序计数器
    线程私有,没有OOM,针对Java程序(Native程序不管)

对象的创建

通过new指令来完成一个对象的创建。有如下操作:

  • 判断对象对应的类是否加载、链接和初始化
  • 分配内存
  • 处理并发安全问题
  • 初始化分配到的内存空间
  • 设置对象头
  • 执行init方法进行初始化

对象的堆内存布局

  • 对象头
    Mark Word、元数据指针
    Mark Word:hash、age(对象的分代年龄)、biased_lock(偏向锁标识位)…
  • 实例数据
  • 对齐填充

oop-klass模型

用来描述Java对象实例的一种模型。

  • OOP(Ordinary Object Pointer)
    指的是普通对象指针,用来表示对象的实例信息。
  • klass
    用来描述元数据

垃圾标记算法

Java中的引用

  • 强引用
    GC时,不回收
  • 软引用
    GC时,不够用才回收
  • 弱引用
    GC时,遇到就回收
  • 虚引用
    GC时,遇到就回收,被回收时会收到一个系统通知。

引用计数法

iOS中Objective-C中使用,不能解决循环引用的问题。

根搜索算法

GC Roots
  • Java栈中引用的对象
  • 本地方法栈中JNI引用的对象
  • 方法区中运行时常量池引用的对象
  • 方法区中静态属性引用的对象
  • 运行中的线程
  • 由引导类加载器加载的对象
  • GC控制的对象

Java对象在虚拟机中的生命周期

  • 创建阶段(Created)
    分配空间——构造对象——构造方法
  • 应用阶段(In Use)
    至少有一个强引用,或者显示的使用软、弱、虚引用
  • 不可见阶段(Invisible)
    在程序中找不到对象的任何强引用。但对象仍可能被特殊的强引用GC Roots持有着,比如对象被贝蒂方法栈中JNI引用或运行中的线程引用等。
  • 不可达阶段(Unreachable)
    在程序中找不到对象的任何强引用,并且垃圾收集器发现对象不可达。
  • 收集阶段(Collected)
    垃圾收集器发现对象不可达,并且一记准备好要对对象的内存空间重新进行分配,如果对象重写了finalize方法,则会调用该方法。
  • 终结阶段(Finalized)
    在对象执行完finalize对象后仍然是不可达状态,或者没有重写此方法,则对象进入终结阶段,并等待被回收。
  • 对象空间重新分配阶段(Deallocated)

垃圾收集算法

标记-清除算法

  • 标记阶段
    标记处可以回收的对象
  • 清除阶段
    回收被标记的对象所占用的空间

缺点:效率低、易碎片。

复制算法

将内存空间分为2部分,每次只使用其中一个区域,在回收时,遍历当前使用区域,将存活的对象复制到另一个区域,然后将当前可回收对象进行回收。

缺点:利用率低。

标记-压缩算法

在标记可回收的对象后将所有存活的对象压缩到内存的一端,是它们紧凑地排列在一起,然后对边界以外的内存进行回收。

分代收集算法

对不同生命周期的对象采取不同的收集策略,根据生命周期长短将它们分别放到不同的区域,并在不同区域采用不同的收集算法。

Java堆区基于分代的概念,分为新生代与老年代,其中新生代还可以细分为Eden空间、From Survivor空间和To Survivor空间。

分代收集
  • Minor Collection:新生代垃圾回收
    当执行一次Minor Collection时,Eden空间的存活对象会被复制到To Survivor空间,在From Survivor空间存活的对象也会被复制到To Survivor空间。
    当对象存活年龄(经历一次GC涨一岁)超过阈值(-XX:MaxTenuringThreeshold)、To Survivor空间已满,则直接复制到老生代(晋级成功)。
    此时,Eden空间与From Survivor空间都会被清空,新生代的对象都在To Survivor空间中。
    最后,From Survivor空间与To Survivor空间互换位置,以确保To Survivor空间是空的。
  • Full Collection:老年代垃圾收回
    老生代会采用标记-压缩算法或标记-清除算法。