内存模型

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,可以分为两种类型:线程共享的方法区、堆,线程私有的虚拟机栈、本地方法栈和程序计数器。

1、 线程私有的数据区: 程序计数器、虚拟机栈、本地方法栈

1) 程序计数器
为了线程切换后能够恢复到正确的执行位置,每条线程都需要一个独立的程序计数器去记录其正在执行的字节码地址。

2)虚拟机栈描述的是Java方法执行的内存模型,是线程私有的。
虚拟机栈有两种异常情况:StackOverflowError 和 OutOfMemoryError。我们知道,一个线程拥有一个自己的栈,这个栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss 参数可以设置虚拟机栈大小),若线程请求的栈深度大于虚拟机允许的深度,则抛出 StackOverFlowError 异常。

3)本地方法栈
和Java虚拟机栈相似,区别在于虚拟机栈执行Java方法服务,而本地方法为虚拟机执行Native方法服务。与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

2、 线程共享的数据区
Java堆、方法区

1)Java堆唯一目的就是存放对象实例,几乎对象实例(和数组)都在这里分配内存,这些对象通过new、newarray等指令建立,不需要程序代码来显式的释放。

2)方法区
方法区与Java堆一样,也是线程共享的并且不需要连续的内存,其用于存储已被虚拟机加载的 类信息、常量、静态变量、即时编译器编译后的代码等数据。

超级推荐阅读open in new window

垃圾回收机制

Java所谓的自动内存管理最终可以归结为自动化地解决两个问题:给对象分配内存和回收分配给对象的内存。

判断一个对象是否可以被回收的两种经典算法
四种典型的垃圾回收算法和直接应用----垃圾收集器

核心问题:

哪些内存需要回收?(判断一个对象是否可以被回收的两种经典算法)

  • 引用计数法
  • 可达性分析算法

什么时间回收?

  • 堆的新生代、老年代、永久代的垃圾回收时机
  • MinorGC、FullGC

如何回收?

  • 标记清除算法
  • 复制算法
  • 标记整理算法

分代整理算法和七种垃圾收集器

非常推荐阅读open in new window

如何确定一个对象是否可以被回收?

  • 1、引用计数算法:判断对象的引用数量

    • 这种算法是垃圾收集器早期策略。在这种方法中,堆中的每个对象实例都有一个引用计数。

    • 任何引用计数为0的对象实例可以被当作垃圾收集。

    • 问题:

      • 很难解决对象间相互循环引用的问题。
  • 2、可达性分析算法:判断对象的引用链是否可达

    • 从离散数学中的图论引入,程序把所有的引用关系看作一张图,通过一系列的名为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。当一个对象到 GC Roots 没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可用的,如下图所示。在Java中,可作为 GC Root 的对象包括以下几种:

    • 虚拟机栈(栈帧中的局部变量表)中引用的对象;

    • 方法区中类静态属性引用的对象;

    • 方法区中常量引用的对象;

    • 本地方法栈中Native方法引用的对象;

可达性分析

垃圾收集算法

  • 1、标记清除算法 该算法首先从跟集合进行扫描,对存活的对象标记。标记完毕后,再扫描整个空间中未被标记的对象进行回收。

    • 标记

    • 清除

    • 不足:

      • 效率问题
      • 空间问题
  • 2、复制算法

    • 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
  • 3、标记整理算法

    • 标记整理算法的标记过程类似标记清除算法,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,类似于磁盘整理的过程,该垃圾回收算法适用于对象存活率高的场景(老年代)。
  • 4、分代收集算法

    • 不同的对象的生命周期(存活情况)是不一样的,而不同生命周期的对象位于堆中不同的区域,因此对堆内存不同区域采用不同的策略进行回收可以提高 JVM 的执行效率。当代商用虚拟机使用的都是分代收集算法:新生代对象存活率低,就采用复制算法;老年代存活率高,就用标记清除算法或者标记整理算法。Java堆内存一般可以分为新生代、老年代和永久代三个模块,

小结:

垃圾回收机制

每日学习,每日精进!