文章目录

Java内存区域

Java虚拟机会在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域

JVM-Java内存区域_常量池

线程私有

线程私有区域是随着线程的创建而创建,随着线程的销毁而销毁,每个线程各自独立,互不干扰

程序计数器

作用:

  • 当前线程执行的字节码的行号指示器
  • 程序控制流的指示器

此区域是唯一不会出现OOM的区域

虚拟机栈

虚拟机栈是Java方法执行的线程内存模型

  • 每个方法执行时,Java虚拟机栈都会同步创建一个栈桢(存储局部变量表,操作数栈,动态链接,方法出口等)
  • 方法从被调用到执行完成返回,就是一个栈桢入栈到出栈的过程

线程共享

随虚拟机进程的启动而一直存在

堆是虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,此区域的唯一目的就是存放对象和数组实例

Java堆是垃圾收集器管理的内存区域,也叫GC堆

堆分代

  • Hotspot虚拟机基于经典堆分代来设计
  • 新生代(一个eden,两个survivor)老年代
  • 分代的根本原因还是为了能更好的回收内存,更高效的回收和减少内存碎片

如果在Java堆中没有内存完成实例分配,并且堆中也无法再扩展时,就会抛出OOM

方法区

  • 方法区也是线程共享的区域
  • 用于存储已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据
  • JDK8之前是存放在永久代,JDK8后存放在本地内存(实现了元空间),废弃了永久代的概念
  • 永久代是在堆中的,如果类型信息,常量,静态变量,代码缓存一多,可能会导致堆OOM,如果放在本地内存,发生OOM的概率相对低很多

运行时常量池

  • 运行时常量池是方法区的一部分,常量池表用于存放编译器生成的各种字面量和符号引用
  • 既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存 时会抛出OutOfMemoryError异常。

对象创建

  • 当虚拟机遇到一条字节码new指令时,首先会去常量池中定位该类的符号引用,判断该符号引用代表的类是否被加载,解析,初始化过,没有则进行类加载
  • 类加载检查通过后,为新生对象分配内存(对象所需内存大小在类加载完成后就可以知道)
  • 指针碰撞:假设Java堆中内存是绝对规整的,所有被使用过的内存都被放在一 边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那 个指针向空闲空间方向挪动一段与对象大小相等的距离
  • 空闲列表:如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那 就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分 配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录

指针碰撞的线程安全问题

  • 正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况
  • 解决:CAS

对象内存布局

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例 数据(Instance Data)和对齐填充(Padding)。

HotSpot虚拟机对象头的包括两类信息:

  • 对象自身运行时数据(hashcode,gc分代,锁标志,是否偏向,偏向线程ID)Markword
  • 类型指针