美文网首页Java语言基础
JVM 内存模型与垃圾回收

JVM 内存模型与垃圾回收

作者: SHAN某人 | 来源:发表于2018-06-11 15:09 被阅读46次
JVM

1. JVM 内存模型

JVM内存模型

1.1 程序计数器

  • 概述: 该区域分配了一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
  • 作用: 通过改变计数器的值来指定下一条需要执行的字节码指令。
  • 特点: 1.线程私有 2. 唯一无内存溢出的区域

1.2 虚拟机栈 (JVM Stack)

  • 概述: Java 方法执行的内存模型,每一个方法从调用直至执行完成的过程,对应着一个栈帧在虚拟机中从入栈到出栈的过程。
  • 作用: 创建一个栈帧用于存储 局部变量表操作数栈动态链接方法出口等信息; -Xss 调整栈大小
  • 特点: 1.线程私有 2. 生命周期与线程执行结束一致
局部变量表
  • 作用: 存放编译期可知的各种基本数据类型(8 种)、对象引用、returnAddress类型(指向了一条字节码指令的地址)。
  • 占用空间:64 位bit长度的long 和double 类型占用两个局部变量空间(Slot),其余类型只占用一个。
  • 分配时机:在编译期间完成分配,当进入一个方法时,该方法需要在帧中分配多大的局部变量空间时完全确定的,在方法运行期间不会改变局部变量空间分配的大小。
虚拟机栈异常
  • SOF 栈溢出:线程请求的栈深度大于虚拟机所允许的深度时抛出该异常。
  • OOM 内存溢出:虚拟机允许栈深度动态扩展,但是扩展时无法申请到足够的内存。

1.3 本地方法栈

  • 概述: 与虚拟机栈类似,是为虚拟机使用到的Native方法服务的内存区域。
  • 区别:
    • 虚拟机栈为虚拟机执行Java方法服务;
    • 本地方法栈为虚拟机使用到的Native方法服务。
  • 异常: 与虚拟机栈一致。

1.4 Java堆(Java Heap)

  • 创建时间:JVM启动时创建该区域
  • 占用空间:该区域是JVM管理内存最大一块
  • 作用: 存放对象实例和数组
  • 特点:
    • 线程间共享
    • GC主要区域
    • 内存空间可扩展,通过设置 xms (最小值) xmx(最大值),通常设置成一样大,避免运行时频繁调整大小

1.5 方法区 (Method Area)

  • 描述:虚拟机规范将它描述为堆的一个逻辑部分,但是它有个别名叫 Non-Heap,目的是与Java堆区分开来。

  • 作用: 存放虚拟机加载的 类信息、常量、静态变量、即时编译器编译的代码等数据。

  • 特点:

    • 线程间共享
    • 可通过 -XX:MaxPermSize 调整其大小
  • 异常:方法区无法满足内存分配需求时抛出 OOM异常

1.6 运行时常量池

  • 描述:方法区的一部分,Class文件中除了有类的(版本、字段、方法、接口)等描述信息外,还有一项信息是常量池。

  • 作用: 存放编译器生成的各种字面量和符号引用。

  • 动态性:

    • Java语言并不要求常量池只有编译期才能产生,也就是并非预置入Class文件中的常量池内容后才能进入方法区的运行时常量池,运行期间也可以将新的常量放入池中。这种特性用的比较广泛的便是String 类的intern()方法。
  • 异常:方法区无法满足内存分配需求时抛出 OOM异常(与方法区一致)。

1.7 直接内存

  • 描述:1.非虚拟机运行时数据区一部分,也不是Java虚拟机规范定义的内存区域。2. 频繁使用,可能导致OOM。

  • 作用:

    • 1.4开始引入NIO可以使用 Native函数库直接分配堆外内存;通过存储在Java堆中的DirectByteBuffer对象作为直接内存的引用,这样做的好处是可以显著提高性能,避免在java堆和 Native堆之间来回复制数据。
  • 异常:收到物理内存限制,导致扩展时申请不到新的内存空间出现OOM

2. 垃圾回收 GC

2.1 回收的对象? 什么时候回收 ?

在垃圾回收之前,我们必须确定的一件事就是对象是否存活?这就牵扯到了判断对象是否存活的算法了。

引用计数算法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器+1,当引用失效,计数器-1.任何时刻计数器为0的对象就是不可能再被使用的。

优点:实现简单,判定效率高效,被actionscript3和python中广泛应用。

缺点:无法解决对象之间的相互引用问题。java没有采纳

可达性分析算法:

通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GCRoots没有任何引用链相连的时候,则证明此对象是不可用的。

比如如下,右侧的对象是到GCRoot时不可达的,可以判定为可回收对象。

可达性分析算法

在java中,可以作为GCRoot的对象包括以下几种:

  • 虚拟机栈中引用的对象。

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

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

  • JNI(Native)方法中引用的对象。


例子:

类中的静态变量

public AClass{
 
  public static Something;
  public static final Apple;
   ''''''
}

Java方法的参数和方法中的局部变量,可以作为root.

public Aclass{

public void doSomething(Object A){
    ObjectB b = new ObjectB; 
    }
 }

基于以上,我们可以知道,当当前对象到GCRoot中不可达时候,即会满足被垃圾回收的可能。

那么是不是这些对象就非死不可,也不一定,此时只能宣判它们存在于一种“缓刑”的阶段,要真正的宣告一个对象死亡。至少要经历两次标记:

第一次:对象可达性分析之后,发现没有与GCRoots相连接,此时会被第一次标记并筛选。

第二次:对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,此时会被认定为没必要执行。

2.2 如何回收? How?

上述的两点讲解之后,我们大概明白了,哪些对象会被回收,以及回收的依据是什么,但回收的这个工作实现起来并不简单,首先它需要扫描所有的对象,鉴别谁能够被回收,其次在扫描期间需要 ”stop the world“ 对象能被冻结,不然你刚扫描,他的引用信息有变化,你就等于白做了。

2.2.1 分代回收

我们从一个object1来说明其在分代垃圾回收算法中的回收轨迹。

1、object1新建,出生于新生代的Eden区域。

2、minor GC,object1 还存活,移动到Fromsuvivor空间,此时还在新生代。

3、minor GC,object1 仍然存活,此时会通过复制算法,将object1移动到ToSuv区域,此时object1的年龄age+1。

4、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象并没有达到survivor的一半,所以此时通过复制算法,将fromSuv和Tosuv 区域进行互换,存活的对象被移动到了Tosuv。

5、minor GC,object1 仍然存活,此时survivor中和object1同龄的对象已经达到survivor的一半以上(toSuv的区域已经满了),object1被移动到了老年代区域。

6、object1存活一段时间后,发现此时object1不可达GcRoots,而且此时老年代空间比率已经超过了阈值,触发了majorGC(也可以认为是fullGC,但具体需要垃圾收集器来联系),此时object1被回收了。fullGC会触发 stop the world。

在以上的新生代中,我们有提到对象的age,对象存活于survivor状态下,不会立即晋升为老生代对象,以避免给老生代造成过大的影响,它们必须要满足以下条件才可以晋升:

1、minor gc 之后,存活于survivor 区域的对象的age会+1,当超过(默认)15的时候,转移到老年代。

2、动态对象,如果survivor空间中相同年龄所有的对象大小的综合和大于survivor空间的一半,年级大于或等于该年级的对象就可以直接进入老年代。

以上采用分代垃圾收集的思想,对一个对象从存活到死亡所经历的历程。期间,在新生代的时刻,会用到复制算法,在老年代时,有可能会用到标记-清楚算法(mark-sweep)算法或者标记-整理算法,这些都是垃圾回收算法基于不同区域的实现,我们看下这几种回收算法的实现原理。

2.2.2 垃圾回收算法

标记清除法(Mark-Sweep)

标记清除法是垃圾回收算法的思想基础。标记清除算法将垃圾分为两个阶段:标记阶段和清除阶段。

标记阶段,通过根节点,标记所有从根节点开始的可达对象,未标记过的对象就是未被引用的垃圾对象。

清除阶段,清除所有未被标记的对象。

标记-清除

复制算法(Copying)

复制算法是,将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在适用的内存中存活对象复制到未使用的内存块,然后清除使用的内存块中所有的对象。

复制算法

IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1 : 1的比例来划分内存空间,而是将内存分为一块较大的Eden空间(比例8)和两块较小的Survivor空间(比例1),每次使用Eden和其中一块Survivore。

标记整理算法(Mark-Compact)

标记-整理

标记压缩算法是一种老年代的回收算法。

标记阶段和标记清除算法一致,对可达对象做一次标记。

清理阶段,为了避免内存碎片产生,将所有的存活对象压缩到内存的一端。

2.2.3 垃圾收集器

垃圾收集器是内存回收的具体实现,不同的厂商提供的垃圾收集器有很大的差别,一般的垃圾收集器都会作用于不同的分代,需要搭配使用。以下是各种垃圾收集器的组合方式:

垃圾回收器搭配

各种组合的优缺点:

新生代GC策略 老年代GC策略 说明
组合1 Serial Serial Old Serial和Serial Old都是单线程进行GC,特点就是GC时暂停所有应用线程。
组合2 Serial CMS+Serial Old CMS(Concurrent Mark Sweep)是并发GC,实现GC线程和应用线程并发工作,不需要暂停所有应用线程。另外,当CMS进行GC失败时,会自动使用Serial Old策略进行GC。
组合3 ParNew CMS 使用-XX:+UseParNewGC选项来开启。ParNew是Serial的并行版本,可以指定GC线程数,默认GC线程数为CPU的数量。可以使用-XX:ParallelGCThreads选项指定GC的线程数。如果指定了选项-XX:+UseConcMarkSweepGC选项,则新生代默认使用ParNew GC策略。
组合4 ParNew Serial Old 使用-XX:+UseParNewGC选项来开启。新生代使用ParNew GC策略,年老代默认使用Serial Old GC策略。
组合5 Parallel Scavenge Serial Old Parallel Scavenge策略主要是关注一个可控的吞吐量:应用程序运行时间 / (应用程序运行时间 + GC时间),可见这会使得CPU的利用率尽可能的高,适用于后台持久运行的应用程序,而不适用于交互较多的应用程序。
组合6 Parallel Scavenge Parallel Old 是Serial Old的并行版本
组合7 G1GC G1GC -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC #开启-XX:MaxGCPauseMillis =50 #暂停时间目标-XX:GCPauseIntervalMillis =200 #暂停间隔目标-XX:+G1YoungGenSize=512m #年轻代大小-XX:SurvivorRatio=6 #幸存区比例

插入:各垃圾收集器的详细说明:
https://www.jianshu.com/p/ab49398328e9
https://www.jianshu.com/p/becfa1f2a9e9


2.3 新生代转移到老年代的触发条件?

  • 长期存活的对象

  • 大对象直接进入老年代

  • minor gc后,survivor仍然放不下

  • 动态年龄判断 ,大于等于某个年龄的对象超过了survivor空间一半 ,大于等于某个年龄的对象直接进入老年代

2.4 什么时候触发Full GC ?

空间分配检查和GC时机
  • 没有配置 -XX:+DisableExplicitGC情况下 System.gc()方法的调用
  • 老年代空间不足:老年代最大可用空间小于历史平均水平
  • 永生代空间不足(JVM规范中运行时数据区域中的方法区,在HotSpot虚拟机中又被习惯称为永生代或者永生区,Permanet Generation中存放的为一些class的信息、常量、静态变量等数据),达到MaxMetaspaceSize阈值;
  • GC时出现promotion failed和concurrent mode failure
  • 统计得到的Minor GC晋升到旧生代平均大小大于老年代剩余空间
    堆中分配很大的对象

promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;
concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。

参考链接
你的JVM还好吗?GC初步诊断--朱小厮的博客

相关文章

  • JVM

    JVM之内存模型JVM之对象定位与访问JVM之Java垃圾回收机制JVM之类加载

  • JVM相关小结

    对JVM中分层模型、垃圾回收期、垃圾回收算法趁着周末小结一下。有不对的地方,还请指正和讨论~ 1.JVM内存模型 ...

  • JVM:性能调优

    JVM内存模型,垃圾回收算法介绍 根据Java虚拟机规范,JVM将内存划分为: -- 年轻代(New) :年轻代用...

  • JVM 内存结构 和内存回收算法

    一、JVM 内存模型、GC 1.1GC是啥? GC是垃圾回收机制,java中将内存管理交给垃圾回收机制,这是因为在...

  • 面试模块题

    1.JVM 1.1 JVM的内存模型? 1.2 你们用的什么垃圾回收器? 1.3 serial 和 parnew ...

  • JVM面试常问基础总结

    JVM内存模型 JVM垃圾回收 1. JVM内存模型 线程隔离的三个区:程序计数器:当前线程所执行的行号指示器,指...

  • 九神带你入门JVM(上)

    概述 本篇较长,九神带你从0入门JVM,全文包括包括JVM的分类、JVM垃圾回收综述、JVM的内存模型(Java ...

  • JVM 内存模型与垃圾回收

    1. JVM 内存模型 1.1 程序计数器 概述: 该区域分配了一块较小的内存空间,可以看作是当前线程所执行的字...

  • JVM内存模型与垃圾回收

    内存模型 各部分的功能 这几个存储区最主要的就是栈区和堆区,那么什么是栈什么是堆呢?说的简单点,栈里面存放的是基本...

  • Jvm内存模型与垃圾回收

    内存模型 JVM内存空间包含:方法区、java堆、java栈、本地方法栈。 方法区是各个线程共享的区域,存放类信息...

网友评论

    本文标题:JVM 内存模型与垃圾回收

    本文链接:https://www.haomeiwen.com/subject/eolheftx.html