Pride's Blog

深入理解JVM

· Pride

一、Java内存区域§

1.运行时数据区域§

堆(Heap): JVM 启动时按 -Xmx, -Xms 大小创建的内存区域,用于分配对象、数组所需的内存,由GC管理和回收。

方法区(Method Area): 存储被JVM加载的类信息(字段、成员方法的字节码指令等)、运行时常量池(字面量、符号引用等)、JIT编译后的CodeCach等;JDK8之前Hotspot将方法区存储于永久代堆内存,之后参考JRockit废弃了永久代,存储于本地内存的Metaspace区。

直接内存: JDK1.4 引入 NIO 使用 Native/Unsafe 库直接分配系统内存,使用 Buffer,Channel 与其交互,避免在系统内存与 JVM 堆内存之间拷贝的开销。

2.JVM对象§

a.创建对象§

分配堆内存: 类加载完毕后,其对象所需内存大小是确定的;堆内存由多线程共享,若并发创建对象都通过CAS乐观锁争夺内存,会导致效率很低。所以线程创建时会在堆内存为其分配私有的分配缓冲区(TLAB: Thread Local Allocation Buffer)

b.内存布局§

对象头

对象数据: 各种字段的值,按宽度分类紧邻存储 对齐填充: 内存对象为1个字长整数倍,减少CPU总线周期 验证: openjdk/jol检查对象内存布局

3.内存溢出§

二、垃圾回收§

GC可分解为三个问题: which、when、how

1.GC条件§

a.引用计数法(Reference Counting)§

每个对象都维护一个引用计数器 rc,当通过赋值、传参等方式引用它时 rc++,当引用变量修改指向、离开函数作用域等方式解除引用时 rc--,递减到0时说明对象无法被使用,可以进入回收了流程。

assign(var, obj):
incr_ref(obj)
# self = self 现增再减,避免引用自身导致内存提前被释放
decr_ref(var)
var = obj

incr(obj):
obj.rc++

decr(obj):
obj.rc--
if obj.rc == 0:
    # 断开 obj 与其他对象的引用关系
    remove_ref(obj)
    # 回收 obj 内存
    gc(obj)

b.可达性分析算法(Reachability analysis)§

从肯定不会被回收的对象(GC Roots)出发,向外搜索全局对象图,不可达的对象即无法在被使用,可回收; 常见可作为GC Root的对象有:

c.引用类型§

引用类型 回收时机
强引用 - 只要与 GC Root 存在引用链,则不被回收
软引用 SoftReference 只被软引用所引用的对象,当 GC 后内存依然不足,才被回收
弱引用 WeakReference 只被弱引用所引用的对象,无论内存是否足够,都将被回收
虚引用 PhantomReference 被引用的对象无感知,进行正常 GC,仅在回收时通知虚引用(回调)

2.GC算法§

三、Class结构§

语法§

示例§

字节码§

四、类加载§

类加载过程§

类加载器§

五、执行引擎§

总结§