java内存

java虚拟机内存结构

  • 程序计数器
    程序计数器属于线程私有,存储下一条待执行的指令的地址,实现跳转、循环、分支等功能,程序计数器不存在内存溢出OOM的问题。

  • 虚拟机栈
    虚拟机栈属于线程私有,线程每执行一个方法时都创建一个栈帧,栈帧包含了该方法的局部变量表(包括八大基本数据类型的变量、对象引用)、操作数栈、动态链接、方法出口等信息,每一个方法的执行对应栈帧的入栈和出栈过程。线程如果请求的栈深度大于虚拟机所允许的最大深度,抛出StackOverflowError异常。虚拟机栈动态扩展过程中,如果超过规范的大小,抛出OutofMemoryError异常。

  • 本地方法栈
    本地方法栈属于线程私有,虚拟机栈为执行的java方法服务,本地方法栈为Native方法服务。同虚拟机栈,也可能会抛出SOE和OOM异常。(Native方法???查找)

  • 方法区
    方法区是各个线程共享的内存区域。方法区主要存放类的信息,常量,静态变量。内存不够时,抛出OOM异常。


  • java堆主要存放对象实例和数组。Java堆是垃圾收集器管理的主要区域,在垃圾回收中,将堆划分为新生代、老年代,还可以划分出每个线程私有的分配缓冲区。Java堆物理上不连续,逻辑上连续。内存不够分配内存时,抛出OOM异常。

  • 运行时常量池
    class文件包含类的版本、字段、方法、接口等信息外,还包括常量池信息,指类中的字面量和符号引用。在类加载时常量池存放在java内存中的运行时常量池中。

对象创建过程(hotspot虚拟机)

类加载->内存分配->初始化为0->配置对象->执行init方法

  1. 类加载
    首先检查类是否被加载过,如果没有,必须先执行相应的类加载过程。
  2. 内存分配
    对象所需内存的大小在类加载完成之后便已经确定。内存分配有两种实现方式:
    • 指针碰撞。假设java堆内存是绝对规整的,可以使用的内存在一边,已经使用的在另一边,中间存放着指针作为分界点的指示器,内存分配就是将指针向空闲的内存移动对象大小相等的距离,
    • 空闲列表。java堆中内存可用和空闲的内存是交替的,空闲列表记录堆中哪些内存块是可用的,当需要分配内存时,从空闲列表中找到一块合适的空间分配给对象。
  3. 初始化为0
    内存分配之后,虚拟机将分配到的内存都初始化为零值,这一操作保证了对象实例字段在java代码中可以不赋初始值就可以直接食用,程序能访问到这些字段的数据类型所对应的零值。
  4. 配置对象
    对对象进行必要的设置,包括这个对象属于哪个类,如何找到这个类的元数据信息,对象的哈希码,对象的GC分代年龄等信息,这些信息存放在对象的对象头中。
  5. 方法
    执行init方法,把对象按照开发者的意愿进行初始化。

对象的内存布局

对象在内存中存储的信息包括对象头,实例数据,对象填充。

  • 对象头
    存储对象运行所需要的数据,如哈希码,GC分代年龄、锁状态标志等。另一部分是类型指针,对象通过类型指针找到所属于的类。
  • 实例数据
    对象存储的有效信息,程序代码中所定义的字段内容。
  • 堆起填充
    起占位符作用。对象的起始地址必须是8的整数倍。

对象的访问定位

  • 句柄访问
    堆中存放着某个对象的句柄池,引用则存放句柄池的地址。句柄池中包含指向对象的地址以及指向对象所属类的地址。
  • 指针访问
    引用直接存放对象的地址,
posted @ 2019-06-22 12:08  御心飞行  阅读(168)  评论(0编辑  收藏  举报