JVM内存模型

JVM内存模型

  • 程序计数器

    • 线程私有,当前线程锁执行的字节码的行号指示器
    • 无OutOfMemory错误
  • 虚拟机栈

    • 线程私有,Java方法执行时会创建栈帧,存储 局部变量表,操作数栈,动态链接和方法出口等信息
    • 局部变量表存放各种编译器可知的基本数据类型boolean、byte、char、short、int、float、long、double、对象引用(地址)和returnAddress(下一条字节码指令地址)
    • 局部变量表的内存空间在编译器分配完成,方法运行期间不会改变其大小。
  • 本地方法栈

    • 虚拟机使用本地Native方法服务
    • 有的虚拟机把这部分与虚拟机栈合并
  • Java堆

    • 线程共享,存放对象实例,几乎所有的对象实例以及数组都要在堆上分配
    • 垃圾回收的主要区域,GC堆
    • 可以处在物理不连续的空间上,但是逻辑上要连续
  • 方法区(也叫堆-永久代)

    • 线程共享,存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码

    • 运行时常量池:存放编译器生成的各种字面量和符号引用

Java对象创建

常量池检查

遇到一条New指令后,先去常量池检查这个类是否已经被加载,如果没有,则执行类加载

类加载

内存分配

对象所需内存的大小在类加载后就可以完全确定(通过类元数据信息),然后将同等大小的内存从堆中划分给对象。

分配方法:

  • 规整内存:指针碰撞
  • 不规整内存:空闲列表

分配同步:

  • CAS+失败重试
  • 本地线程分配缓冲

设置对象头

对象内存 = 对象头 + 实例数据 + 对齐填充

对象头一般包括两部分信息:

  • 存储对象自身的运行时数据(hashCode,GC Age, 锁状态等) 一个字长大小,也叫“MarkWord”,非固定结构,根据对象的不同状态,存储不同含义的数据
  • 类型指针,对象指向类元数据的指针。如果是数组,还需要记录数组长度

实例数据:类中所定义的各种类型的字段内容

对齐填充:HotSpot要求对象起始地址必须是8字节的整数倍,对象的大小必须是8字节的整数倍。

访问对象

通过操作栈上的Reference数据来操作堆上的对象,如何实现Reference:

  • 句柄:二层索引,稳定
  • 指针:一层索引,快速高效

垃圾收集

程序计数器和虚拟机栈、本地方法栈随线程生存消亡,内存分配回收相对确定,但是堆上内存就复杂的多,需要设计垃圾回收算法进行回收。

如何判断哪些对象需要被回收

  • 引用计数:无法解决循环引用
  • 可达性分析:GCRoot无法到达该对象,该对象就标为死亡
    • 可以作为GCRoots的结点:栈上引用对象(局部变量),永久代上引用对象(静态变量和常量)
    • 对象在可达性分析中不可达后,不代表一定会消亡,在标记过程中,会触发finalize方法(只会触发一次,没有实现该方法则不触发),如果对象重新与引用链关联上,就不会被回收。这里的触发只是JVM会调用该方法,但不一定等待该方法执行完

方法区回收

  • 废弃常量
    • 没有其他地方引用该常量
  • 无用的类
    • 该类所有实例都已被回收
    • 加载该类的ClassLoader被回收
    • 该类对应的java.lang.class对象没有在任何地方被引用

垃圾回收算法

  • 标记-清除算法

    • 标记所有需要清除的对象,然后再统一回收
    • 存在效率问题和空间问题(内存碎片)
  • 复制算法

    • 内存对半分,用完一半,回收时,将存活对象复制到另外一半,顺序排列
    • 简单高效,但是内存容量缩小
    • 一个大的Eden空间和两个小的survivor空间,每次使用Eden和一个survivor(from),在回收时,将其中还存活的对象一次性复制到另外一个survivor(to)中,当survivor(to)空间不够时,依赖其他内存进行分配担保
  • 标记-整理算法

    • 标记需要清理的对象,然后存货对象向内存一端移动
  • 分代收集算法

    • Java堆划分为新生代和老年代,新生代存储新分配的对象,老年代存放存活周期大和内存空间大的对象
    • 新生代对象代谢频繁,使用复制算法,老年代存活率高,使用另外两种算法。

内存分配回收策略

  • 对象优先分配在Eden
    • 对象将尝试在Eden中分配,如果空间不够,则调用一次MinorGC
    • MinorGC 发生在新生代,很快; MajorGC FullGC 发生在老年代,比较慢
  • 大对象直接进入老年代
    • eg.长字符串和数组
  • 长期存活的对象将进入老年代
    • 对象年龄AGE:每在Survivor区中熬过一次MinorGC就长一岁,默认15岁就要去老年代
    • 动态年龄对象判定:如果在survivor空间中相同年龄的对象大小的总和大于survivor空间的一半,则年龄大于或等于该年龄的就可以直接进入老年代
  • 空间分配担保
    • 老年代为新生代MinorGC提供担保
    • 在复制算法中,如果survivor(to)空间不足,则将剩余的存活对象装入老年代中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值