深入理解Java虚拟机(一): 内存区域

举报
且听风吟 发表于 2019/11/03 20:04:15 2019/11/03
【摘要】 本文带你了解java虚拟机中的内存方式。

运行时数据区域

Java在运行时会把它所管理的内存划分为若干个不同的数据区域。

方法区(Method Area)

      (1)线程共享

      (2)存储

类信息

常量

静态变量

即时编译器编译后的代码

(3)非堆

(4)不需要连续的内存

(5)可以选择固定或可扩展

(6)可选择不实现垃圾收集

(7)针对常量池的回收


  • 运行时常量池(Runtime Constant Pool)

        a、常量池:存放编译期生成的字面常量和符号引用

        b、这些常量池中的数据进入方法区后存放在运行时常量池中

        c、动态性:运行期可将新的常量放入池中,例如:String.intern()方法

(8)对类型的卸载

(9)无法满足内存分配需求时会抛出OutOfMemoryError

虚拟机栈(VM Stack)

        (1)线程私有

        (2)生命周期与线程相同

        (3)描述Java方法执行的内存模型

        (4)栈帧

        a、局部变量表:基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)

        b、操作数栈

        c、动态链接

        d、方法出口

        e、64位的long 和double会占用两个局部变量空间,其余的只占一个

        f、编译期完成内存分配

        g、方法运行期间不会改变局部变量表的大小

        h、存储

        i、入栈

        j、出栈

(5)线程请求的栈深度大于虚拟机允许的深度会抛出StackOverFlowError

(6)如果扩展时无法申请到足够内存会抛出OutOfMemoryError

本地方法栈(Native Method Stack)

    (1)为本地方法(Native Method)服务

    (2)<--> 区别于虚拟机栈:为虚拟机执行Java方法服务。

    (3)抛出StackOverFlowError

    (4)抛出OutOfMemoryError

堆(Heap)

    (1)Java虚拟机所管理的内存中最大的一块
    (2)线程共享
    (3)虚拟机启动时创建
    (4)存放对象实例及数组
    (5)垃圾回收管理(GC)主要区域
         - 分代算法
            | 新生代
                eden
                from
                to
            | 老年代
    (6)可划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)
    (7)可处于物理上不连续的内存空间,保证逻辑连续即可
    (8)可通过-Xmx和-Xms控制大小(可扩展)
    (9)堆无法再扩展时会抛出OutOfMemoryError

程序计数器(Program Counter Register)

    (1)较小内存空间

    (2)当前线程的行号指示器

    (3)线程私有的内存

    (4)唯一一个没有OutOfMemoryError的区域

直接内存(Direct Memory)

    (1)NIO中使用基于通道(Channel)与缓冲区(Buffer)的I/O方式,使用Native函数库直接分配的堆外内存

    (2)无法满足内存分配需求时会抛出OutOfMemoryError

对象的创建

  • (1)类加载检查机制

  • (2)为新生对象分配内存

  • (3)把一块确定大小的内存从堆中划分出来。

    • 选择方式由Java堆是否规整决定

    • Java堆是否规整由垃圾收集器是否带有压缩整理功能决定。

    • 由于对象创建是非常频繁的,则在并发情况下不是线程安全的

    • 对分配内存空间的动作进行同步处理

    • 把内存分配的动作按照线程划分在不同的空间中,即TLAB方式。

    • 只有在TLAB用完并分配新TLAB的情况下需要同步锁定

    • 可以使用-XX:+/-UseTLAB来设定

    • 内存规整: 指针碰撞(Bump the Point)

    • 内存不规整 空闲列表(Free List):维护列表并记录哪些是可用的。

  • (4)将分配到的内存空间初始化为0(不包含对象头)

  • (5)对对象进行必要的设置

  • (6)属于哪个类的实例

  • (7)如何找到类的元数据信息

  • (8)对象的哈希值

  • (9)对象的GC分代年龄

  • (10)这些数据存放在对象头(Object Header)中

  • (11)对象得到创建,但是所有的字段为零,在执行new之后会立即执行方法

对象的内存布局

(1)对象头(Object Header)
    存储对象自身运行时数据(Mark Word),通常在32位和64位的机器上占用32bit和64bit的空间
        哈希吗
        GC分代年龄
        锁状态标志
        线程持有锁
        偏向线程ID
        偏向时间戳
(2)类型指针: 对象指向它的类元数据的指针
    a、虚拟机通过这个指针来确定这个对象属于哪个类

    b、不是所有的对象实例都保留有类型指针
    c、如果对象是数组,还必须有一块用于记录数组长度的数据
(3)实例数据(Instance Data),真正存储的数据
    存储顺序受到虚拟机分配策略参数(Fields Allocation Style)和字段在Java源码中的定义顺序影响
(4)对齐填充(Padding)
    a、不是必要存在的
    b、仅仅起占位作用

对象访问定位

    Java通过栈上的引用(reference)数据来操作堆上的具体对象

句柄访问

(1)堆中划分一块内存作为句柄池

(2)reference中存储对象的句柄地址

(3)句柄中包含对象实例数据与类型数据各自的具体地址信息

优点:reference中存储稳定的句柄地址,在对象移动时只会改变句柄中的实例数据指针

直接指针

(1)考虑如何放置访问类型数据的相关信息

(2)reference中直接存储对象地址

优点:速度更快,节省一次指针定位的时间


本文转载自微信公众号【java学习之道】。

原文链接:https://mp.weixin.qq.com/s?__biz=MzU4NzYwNDAwMg==&mid=2247484119&idx=1&sn=fcacaef42b10bea58bed65a71db998f4&chksm=fde8cb9bca9f428dce205eb6724d1b38f1e8927fe1b5744946854453&scene=0#rd

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。