JVM内存模型及对象的生死

1 JVM内存划分

JVM内存模型及对象的生死_第1张图片

2 分区

JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。

2.1 程序计数器

一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。
正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果还是 Native 方法,则为空。
这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。

2.2 虚拟机栈

是描述 java 方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、
方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

2.3 本地方法区

本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务,
如果一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个C栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一。

2.4 堆-运行时数据区

是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor(S0) 区和 To Survivor (S1)区)和老年代。

2.5 方法区

即我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. HotSpot VM把 GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版
本、字段、方法、接口等描述等信息外,还有一项信息是常量池 。

1.7之后已经移除了永久代,而是元空间

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

  • -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
  • -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
  • -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
  • -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

3 JVM基础

3.1 对象的创建

对于内存不规整的情况,容易发生指针碰撞

对于内存规整的情况,容易产生空闲列表

3.2 对象内存布局

  • 对象头(Mark Word):存储对象自身的运行时数据,如哈希码,GC分带年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳。
    数据长度在32位和64位虚拟机中分别为32bit和64bit。另外一部分是类型指针,即对象指向它的类元数据的指针。
  • 实例数据:真正的有效信息,定义的各种类型字段的内容,父类继承的也会记录。
  • 对齐填充:不是必然存在的,HotSpot要求对象起始地址(对象大小)必须是8的倍数,对象头部分刚好是1倍或2倍,因此当实例数据部分没对齐时,就需要自动填充来补齐。

3.3 对象是否存活

  • 引用计数法:给对象添加一个引用计数器,有一个引用加1,引用失效时减1,计数器为0则对象不再被使用。缺点无法解决循环引用的问题。
  • 可达性分析法:通过一系列的“GC Roots”的对象作为起始点,往下搜索,当一个对象到达GC Roots没有任何引用链相连,则对象不可用,可回收。

GC Roots

  • 虚拟机栈(栈针中的本地变量表)中引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Native方法)引用的对象

3.4 对象的生或死

在可达性分析算法中的不可达对象,也不是非死不可,宣告一个对象的死亡,至少要经过两次标记过程,没有引用链的对象,
会进行第一次标记并且筛选,筛选条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者虚拟机已经执行过,
则视为没必要执行。finalize()只会被系统调用一次。对象有可能在finalize方法中被拯救。

本文由mdnice多平台发布

你可能感兴趣的:(后端)