Skip to content

JVM相关

JVM内存模型

jvm的内存分为线程私有区域线程共享区域

线程私有区域:栈``本地方法栈``程序计数器

栈(Stack):

  • 存储局部变量、操作数栈、方法出口等信息
  • 每个线程都有一个私有的栈,用于存储方法的局部变量和结果
  • 栈(stack)是一种数据结构,他按照先进先出的原理管理数据,即最后进入的元素最先被访问,栈可以看成是一种特殊的线性表,只允许在一端错插入和删除,该端被称为栈顶,而另一端被称为栈底

本地方法栈(Native Method Stack):

  • 与栈类似,用于存储执行本地(native)方法的数据
  • 本地方法栈是Java虚拟机(JVM)内存模型中的一部分,用于支持本地方法的调用。本地方法指的是用非Java语言(如C,C++)编写的通过 Java Native Interface (JNI)在Java程序中调用的方法
  • 在Java程序中,当需要调用本地方法时,JVM会创建一个本地方法栈,用于执行本地方法的操作。与虚拟机栈类似。本地方法栈是线程私有的,也就是说每个线程都有自己的本地方法栈

程序计数器(Program Counter Register):

  • 每个线程都有一个程序计数器,用于存储当前线程正在执行的指令的地址
  • 线程切换时,程序计数器也会切换到相应线程的执行地址

线程共享区域:堆``方法区

  • java中堆是jvm管理的最大的一块内存空间。主要存放对象实例,被线程共享
  • 在JDK1.8之前,分为新生代、老年代、永久代
  • 在JDK1.8之后,分为新生代、老年代,(永久代在jdk1.8被移除,被一个称为”元数据区(元空间)“的区域取代
  • Java堆内存的大小=新生代+老年代(新生代占堆空间的1/3,老年代占3/2)

方法区:

  • 方法区是 JVM 的一个规范,所有虚拟机必须要遵守。常见的JVM虚拟机有Hostpot、JRockit(Oracle)、J9(IBM),方法区逻辑上是属于堆的一部分,但是为了与堆区分又被称为 非堆区
  • 方法去是各个线程共享的主要用于存储已经被虚拟机加载的类型信息、常量、静态变量等信息

JVM 堆及各种 GC 详解

JVM中的堆,一般分为三大部分:新生代、老年代、永久代(在Java 8 中移除

新生代:

Eden区:java新对象的出生地(如果新创建的对象内存占用很大,则直接分配到老年代),在初始阶段,新创建的对象被分配到 Eden 区,survivor 的两块空间都为空,当 Eden 区内存不不够的时候就会触发 Minor GC 堆新生代进行一次垃圾回收

SurvivorFrom(From 区):上一次 GC 的幸存者存放区

SurvivorTo(To 区):本次 GC 的幸存者目标区

GC 开始前必有一个 Survivor 为空,GC 结束后角色强制交换

Eden 和 S0,S1 区的比例为 8 : 1 : 1

幸存者 S0,S1 区:复制之后发生交换,谁是空的,谁就是 SurvivorTo

JVM 每次只会使用 eden 和其中一块 survivor 来为对象服务,所以无论什么时候,都会有一块 survivor 是空的,因此新生代实际可用空间只有 90%

当 JVM 无法为新建对象分配内存空间的时候 (Eden 满了),Minor GC 被触发。因此新生代空间占用率越高,Minor GC 越频繁。

Minor GC 的过程 (复制算法)

  1. 首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄以及达到了老年的标准,一般是 15,则赋值到老年代区)
  2. 同时把这些对象的年龄 + 1(如果 ServicorTo 不够位置了就放到老年区)
  3. 然后,清空 Eden 和 ServicorFrom 中的对象;
  4. 最后,ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom 区。

Minor GC 触发机制:

当年轻代满(指的是 Eden 满,Survivor 满不会引发 GC)时就会触发 Minor GC(通过复制算法回收垃圾)