第2章 Java内存区域与内存溢出异常

第2章 Java内存区域与内存溢出异常

2.1 概述

在这里插入图片描述

2.2 运行时数据区域

2.2.1 程序计数器

  • 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器
  • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
  • Java虚拟机中的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的。在任何一个确定的时间,一个处理器都只会执行一条线程中的指令
  • 为了保证在线程切换后能恢复到正确的执行位置,这个时候每条线程就都需要一个独立的程序计数器,而各线程之间计数器是互不影响,独立存储的
  • 如果一个线程正在执行的是一个Java方法,该计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是本地方法,该计数器值则应为空
  • native关键字修饰的方法即为本地方法,本地方法类似接口方法,不过接口方法是用abstract修饰的。本地方法的方法体不是由java代码写的,而可能由别的语言如c实现,可以理解为连接java代码和其他语言实现的代码的入口。本地方法的方法体不是由java字节码构成,无法应用Java字节码地址的概念。JVM规范规定pc寄存器的值是未定义的
  • 程序计数器是线程私有的,程序计数器也是内存区域中唯一一个不会发生Out Of Memory Error情况的区域
  • 程序计数器需要存储的只是下一条需要执行的命令地址,最坏的情况死循环也不会让内存区域超限,因为它只需要维护那一条指令的地址

2.2.2 Java虚拟机栈

  • Java虚拟机栈也是线程私有的,它的生命周期和线程相同
  • 每一个方法被调用直到执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
  • 如果线程请求的栈深度大于虚拟机所允许的深度,就会抛出StackOverflowError(堆栈溢出错误)异常;如果虚拟机的栈容量是可以动态扩展的,而栈扩展到无法申请到足够内存时,会抛出Out Of Memory Error异常

2.2.3 本地方法栈

  • 本地方法栈和虚拟机栈相似
  • 虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的本地方法服务

2.2.4 Java堆

  • Java堆是虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建,此区域唯一目的就是存放对象的实例
  • Java里几乎所有的对象实例都是在堆中分配内存的
  • 从分配内存的角度上看,所有线程共享的Java堆中又可以划分多个线程私有的缓冲区,又称私有缓存区域
    • 在并发环境下时从堆中划分内存空间是线程不安全的,而使用加锁等机制又会影响分配速度
    • JVM会给每个线程分配一个私有缓存区域,不仅避免了非线程安全问题,还提升对象分配时的效率
  • 根据《Java虚拟机规范》的规定,Java堆可以处于物理上不连续的内存空间中,但在逻辑上应该被视为连续的
  • Java堆可以被实现成固定大小的,也可以是可扩展的,Java堆无法完成实力分配,并且堆也无法扩展时,将会抛出Out Of Memory Error

2.2.5 方法区

  • 方法区和Java堆一样,是各个线程共享的内存区域
  • JDK 1.7时,把原本放在永久代的字符串常量池、静态区移出
  • JDK 1.8时,废弃永久代的概念,改用元空间,将永久代中剩余的内容移到元空间中
  • 使用永久代实现方法区更容易导致Java应用遇到内存溢出的问题
  • 方法区和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾回收
  • 相对而言,垃圾回收行为在这个区域很少出现,并非数据进入永久代就会永久存在
  • 方法区的内存回收目标主要是针对常量池的回收和对类型的卸载
  • 如果方法区无法满足新的内存分配需求时,将抛出Out Of Memory Error异常

2.2.6 运行时常量池

  • 运行时常量池是方法区的一部分,在类加载后存放到该常量池中
  • 运行时常量池的一个重要特征是具备动态性,Java语言并不一定要求只有编译期才能产生,运行期间也可以将新的常量放入常量池中,如String类的intern()方法

2.2.7 直接内存

  • 直接内存并不是虚拟机运行时数据区的一部分,也不是规范中的内存区域
  • JDK 1.4新加入的NIO是一种基于通道和缓冲区的IO方式,可以使用Native数据库直接分配堆外内存,然后通过一个Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
  • 这样能避免Java堆和Native堆中来回复制数据

2.3 HotSpot虚拟机对象探秘

2.3.1 对象的创建

2.3.2 对象的内存布局

  • 在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三部分
    • 对象头
      • 一部分是用于存储对象自身的运行时数据
        • 哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等——Mark Word
      • 一部分是类型指针
        • 即对象指向它的类型元数据的指针,Java虚拟机通过这个指针确定该对象是哪个类的实例
        • 并不是所有的虚拟机实现都必须在对象数据上保留类型指针
    • 实例数据
      • 对象真正存储的有效信息,即在程序代码中所定义的各种类型的字段内容,无论是从父类继承下来的,还是子类中定义的字段都必须记录清楚
      • 这部分的存储顺序会受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响。HotSpot虚拟机默认的分配顺序为:longs/doubles、ints、shorts/chars、bytes/booleans、oops
      • 相同宽度的字段总是被分配到一起存放,满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前
      • 可以通过设置参数,将子类之中较窄的变量允许插入父类变量的空隙之中,节省出一点空间
    • 对其填充
      • 不是必然存在的,也没用特别的含义,仅仅其占位符的作用
      • HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,即任何对象的大小都必须是8字节的整数倍
      • 对象头部分已经被精心设计成8字节的倍数,如果对象实例数据部分没用对其就需要对其填充来补全

2.3.3 对象的访问定位

  • Java程序会通过栈上的reference数据来操作堆上的具体对象,在《Java虚拟机规范》中只规定它是一种指向对象的引用,并没有定义使用什么方式,因此对象的访问方式是由虚拟机实现而定的

《深入理解Java虚拟机》

周志明 著
机械工业出版社
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值