• 快速理解 JVM 内存模型 & 对象组成 & 对象内存分配


    快速理解 JVM 内存模型 & 对象组成 & 对象内存分配

    JVM 内存模型

    JVM 内存模型分为首先在线程纬度可以分为两部分

    一部分是 线程共享: 堆、元空间

    • 堆 : 大多数 new 的对象都存在于堆内,也是 GC 主要回收的空间,占据 JVM 内存的大部分
    • 元空间:存储静态信息,如 常量、静态变量、类元信息(你写的类解析出来的字节码数据),该部分内存空间是直接内存空间。在 cpu 执行权限上,内存分为 用户空间内核空间,对于 JVM 角度来说即 JVM 堆内存区域系统直接内存区域 感兴趣可以浏览文章 零拷贝

    一部分是 线程独有: 线程栈、本地方法栈、程序计数器

    • 线程栈: 细分栈帧,线程在执行方法的过程中,每调用一个方法即为该方法分配一块栈帧内存空间,方法调用结束则弹出该栈帧释放栈帧内存空间,每个线程栈空间有限,当你出现循环调用,不停的往线程栈中压入栈帧时,最终就会触发 StackOverFlow 异常。可通过 -Xss 参数配置 (线程栈使用的也是非堆内存,即直接内存)
    • 程序计数器:记录当前线程方法执行位置,用于在线程切换后恢复现场继续执行。
    • 本地方法栈:java 调用本地方法(其他语言的方法,如 C 语言方法)单独区分的栈空间,原理类似于线程栈,有些虚拟机会将其和 线程栈合成一个使用。

    在这里插入图片描述

    对象组成

    在 Java 当中对象在存储时会分为五个部分

    1. Mark Word:属于对象的固有属性,占用 8 byte (字节 ps 1 byte = 8 bit 位)
    2. Klass Pointer 对象指针, 占用 4 byte (开启指针压缩后,Java 1.6 版本后默认开启)。
    3. 数组长度,只有数组对象有,记录数组长度 占用 4 byte。
    4. 实例数据: 这个是我们在定义类时的内部成员变量的占用,是我们开发时经常操作看的到的。
    5. 对其填充: 有时有,会将对象大小补齐为 8 byte 的倍数。

    所以,对于一个对象,即使你啥都没有声明,new 出来都至少要占用 8(mark word)+4(klass pointer)+4(对其填充) = 16 byte

    说说指针压缩

    首先 CPU 分为 32 位寻址 和 64 位寻址,32 位的 CPU 只能处理 32 位故而只能在 2^32 = 4G 内存中寻址,所以 32 位机器和系统只能支持 4G 的内存,现在大部分机器都是 64 位即 2^64 = 18446744073709551616 可支持的理论内存是绝对够人类使用了。

    那在 64 位系统下,理论上 JVM 表示一个对象所在内存的地址需要 64 位来表示即 8 byte 字节,那其实大可不必🙅🏻‍♀️

    8G 寻址 = 2^33 ,16G 寻址 = 2^34 ,32G 寻址 = 2^35 ,而我们大部分机器基本都在 32G 或之内,所以基本 35 位就可以表示所有内存位置了,JVM 通过算法讲其压缩到 32 位,在到 CPU 寄存器处理时在逆向解压缩出来。指针压缩就是这个道理。

    Tips:默认配置下,如果堆内存大于 32G 指针压缩会失效,一般我们最大设置堆内存会设置的比 32G 小一点点,这样可以避免很多麻烦,所以堆并不是越大越好。

    指针压缩和类型的对其填充有关,默认对其填充 8 byte 倍数,调高该值可以使指针压缩支持到更大。参考 为什么JVM开启指针压缩后支持的最大堆内存是32G?

    对象内存分配

    在给对象分配内存的时候,有两种方式

    1. 指针碰撞: 这个是默认的方式,用于规整的 JVM 内存空间进行内存分配 (比如垃圾回收算法使用的是标记整理),这种依次分配的效率是比较高的
    2. 空闲列表:当内存并不规整,而是分散使用时,就需要维护一个空闲位置的列表地址进行分配 (标记清除)

    那在分配内存的时候,多线程情况下不可避免会出现抢占问题,解决方案:

    1. 线程本地缓冲 TLAB Thread Local Allocate Buffer: 即预先给每个线程分配一块空间,有些在自己分配的空间中进行分配避免争抢
    2. 比较交换 CAS Compare And Swap + 失败重试:即多个线程争抢一个位置时,先拿到的先分配,其他的失败后重试其他位置分配
  • 相关阅读:
    【Mysql性能优化系列】MySQL优化WHERE语句18个硬核技巧
    大话Redis(1)
    机器学习笔记之最优化理论与方法(四) 凸函数:定义与基本性质
    RH850P1X芯片学习笔记-Generic Timer Module -ATOM
    浅谈static_cast、dynamic_cast、const_cast、reinterpret_cast用法
    解释一下分库分表的概念和优缺点。如何设计一个高性能的数据库架构?
    Linux基础篇之文件权限问题讲解
    redis工具类
    【Python搜索算法】广度优先搜索(BFS)算法原理详解与应用,示例+代码
    直播课堂系统08-腾讯云对象存储和课程分类管理
  • 原文地址:https://blog.csdn.net/w903328615/article/details/127974674