【JVM】内存区域 & 对象创建定位

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Francis123580/article/details/82355478

运行时数据区域

Java虚拟机在执行Java程序中把内存划分为若干个不同的数据区域
不同的区域有各自的用途,创建时间和销毁时间
这里写图片描述

方法区

  • 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码
  • 各个线程共享区域
  • 别名:非堆、永久代

去永久代原因

  1. 字符串在永久代中,容易出现性能问题和内存溢出
  2. 类和方法的信息难以确定大小,太小导致永久代溢出,太大导致老年代溢出
  3. 永久代为GC带来复杂度,使回收效率变低

去永久代过程

  1. JDK1.7将常量、静态变量放入堆中
  2. JDK1.8将类信息、编译代码放入元空间中

  • 用于存放对象实例和数组,在JVM启动时创建
  • JVM管理内存中最大的一块
  • 所有线程共享的内存区域
  • 从GC角度来看,还可以细分为新生代(Eden、FromSurivivor、ToSurvivor)、老年代

程序计数器

  • 通过改变计数器的值选取下一条需要执行的字节码指令
  • 一块较小的内存空间,不会发生OutOfMemoryError的区域
  • 线程私有内存,各线程计数器互不影响

虚拟机栈

  • 用于方法执行时创建栈帧,存储局部变量表、操作数栈、动态链接、方法出口
  • 线程私有,生命周期和线程相同
  • 线程请求栈深度大于允许深度会抛出StackOverflowError,动态扩展无法申请到足够内存会抛出OutOfMemoryError

本地方法栈

  • 和虚拟机栈不同点:本地方法栈执行本地方法,虚拟机栈执行Java方法
  • 和虚拟机栈相同点:抛出StackOverflowError和OutOfMemoryError

对象创建过程

  1. 检查new指令的参数是否能在常量池中定位到一个类的符号引用
  2. 检查这个符号引用代表的类是否已被加载、解析和初始化,若没有则执行类加载
  3. 为新生对象分配内存(采用CAS+失败重试保证多对象同时分配的原子性;或使用TLAB)
  4. 把分配的内存空间都初始化为零值(保证了对象的实例字段没有初始值就可以直接使用)
  5. 对对象进行必要设置,放在对象头中(类信息、元数据信息、哈希码、GC年龄)
  6. JVM工作完成,然后按照程序员意愿进行初始化

对象内存布局

对象头:

  • 对象自身运行时数据:HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID
  • 类型指针:对象指向它的类元数据的指针
  • 数组长度数据:如果对象是Java数组,它的对象大小从元数据中无法确定

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

对象访问定位

通过栈上的reference数据操作堆上的具体对象,Sun HotSpot使用的是直接指针访问

句柄访问

  • reference指向堆中划分的句柄池
  • 句柄池存放指向堆里对象实例数据的指针
  • 句柄池存放指向方法区里对象类型数据的指针

直接指针访问

  • reference指向堆中对象实例地址
  • 对象实例中存放指向方法区的对象类型数据的指针

猜你喜欢

转载自blog.csdn.net/Francis123580/article/details/82355478
今日推荐