JVM组成详解

Posted Ferron Zhu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM组成详解相关的知识,希望对你有一定的参考价值。

1、JVM的堆

1.1、概述

JVM中的堆内存是JVM中占用内存最大的地方,它的内存是可以调节的。一个JVM中只有一个堆内存,它是存放Java中所有的实例对象和数组。它在逻辑上包括新生区、老年区和永久区(JDK版本不同,会有区别),实际上JVM的总内存=新生区+老年区,物理上永久区并不存在。
在这里插入图片描述

1.2、新生区

​ 新生区是一个实例对象出生、生存甚至死亡的地方,绝大部分的实例对象都在新生区被GC机制所释放。新生区包括三部分,分别为伊甸园区、幸存0区和幸存1区;当伊甸园区被GC后,存活下来的实例对象则进入幸存0区或幸存1区,但是幸存0区和幸存1区是可交换的,关于这两者会在后面说明。

1.3、老年区

​ 当实例对象在新生区没有被GC机制释放时,则会进入老年区,老年区也存在GC机制。

1.4、永久区

​ 永久区(代)的概念其实涉及到另外两个概念:方法区和元空间,永久代和元空间是对方法区的实现方式而已,通俗来说就是永久代和元空间都属于方法区。因此我们接下来只讨论永久代和元空间,他们两个都属于方法区。JDK6及之前,永久代存在,而永久代包括常量池;当JDK7,提出了一个去永久代的概念,永久代就慢慢退化了,而永久代的常量池也移到了JVM的堆内存中;在JDK8中,就没有永久代这一说法了,而是改名叫做元空间。

1.5、OOM

​ 熟悉JVM调优的人应该都知道OutOfMemoryError(OOM),该异常的意思是Java堆内存满了,出现该问题的原因就是当幸存区的对象被移到老年区,老年区触发GC后,仍然无法清除出空间,此时会出现OOM异常。分析该异常出现原因的步骤:

  • 尝试手动扩大Java堆内存再尝试
  • 如果扩大内存之后还出现OOM,则可能是代码逻辑出现问题,可以使用专业分析工具JPofiler来进一步判断。

2、JVM中的栈

JVM中包括两种栈:JVM栈和本地方法栈,两种栈其实本质差不多,区别在于一种是用来加载类方法,另外一种则是用来加载本地方法。

2.1、JVM栈

​ JVM栈主管某个线程的开始至终止。一个线程开始,其main方法(假设存在)会首先以栈帧的形式压入栈的最底部,然后接着其他方法的栈帧压入,当一个方法完成,则该方法的栈帧出栈,最终当main方法的栈帧出栈后,该进程终止,该JVM栈被释放。

​ 下面这图详细说明了栈帧的构成。
在这里插入图片描述

2.2、本地方法栈

​ 本地方法栈与JVM栈的构成类似,只不过其调用的是本地方法(native)。凡是带了native的关键字,说明java的作用范围达不到了,它会去调用底层C语言写的库,该方法被调用后会进入本地方法栈,此时本地方法栈会通过JNI(java本地方法接口)去调用本地方法库,JNI做的优点就是**拓展了java的使用,融合不同的编程语言为java使用,最初就是为了调用c和c++。**它会在内存中专门开辟一块标记区域:本地方法栈,它的目的是为了登记本地方法。

3、PC寄存器

​ PC寄存器也就是程序计数器,每个线程都是一个程序计数器,是线程私有的,就是一个指针,指向方法区的方法字节码(用来存储指向下一条指令的地址,也就是即将要执行的指令代码),在执行引擎读取下一条指令。

4、GC机制

4.1、两种GC

  • Minor GC:发生在新生区的GC,GC主要通过复制算法实现
  • Major GC:也叫Full GC,发生在老年代。通常通过标记清除或标记整理算法实现。

4.2、GC机制算法

介绍GC机制算法之前,我们先来介绍几种基本GC算法

  • 复制算法:
    • 新生区有两个幸存区,我们假设两个幸存区分别为幸存0区和幸存1区,刚开始都为空。伊甸园满了之后触发minor gc。
    • 每次gc都会将伊甸园中活的对象移到幸存0区或幸存1区,我们假设此时移入幸存0区。此时伊甸园为空。此时我们把幸存0区叫做from区,1区叫做to区。
    • 伊甸园又满了触发第二次gc。如果有活的对象进入另外一个空的幸存区,即幸存1区。此时则把原来幸存0区的元素复制到幸存1区,幸存0区为空。此时幸存1区为from区,幸存0区为to区。
    • 当一个对象经历了15次gc之后还没被清除,则进入老年代。该阈值MaxTenuringThreshold可以被调整。注:伊甸园区放不下,触发minor gc放到幸存区,幸存区放不下放到老年区,老年区放不下触发major gc,major gc没有则出现OOM
    • 复制算法保证其中有一个幸存区为空;复制算法适合存活率较低的地方:新生区
    • 好处:不会有内存碎片化
    • 坏处:需要额外的空间
  • 标记清除算法:JVM通过引用计数法或者可达性分析算法来判断一个对象是否被引用,当其不被引用时,则将其清除
    • 好处:不需要额外的空间
    • 坏处:会产生内存碎片,两次扫描严重浪费时间
  • 标记压缩(整理)算法:标记清除算法的优化,再次扫描,向一端移动存活的对象,防止内存碎片的产生
    • 好处:不会产生内存碎片
    • 坏处:多一次移动成本

GC算法:分代收集算法

  • 年轻代:实例对象存活率低,使用复制算法
  • 老年代:区域大,实例对象存活率高,标记清除+标记压缩混合实现(多少次清除后进行一次压缩是JVM中GC算法调优的一个参数)

活率低,使用复制算法

  • 老年代:区域大,实例对象存活率高,标记清除+标记压缩混合实现(多少次清除后进行一次压缩是JVM中GC算法调优的一个参数)

以上是关于JVM组成详解的主要内容,如果未能解决你的问题,请参考以下文章

详解Jvm内存结构

JVM组成详解

JVM组成详解

Java GC的工作原理详解

JVM1.6 GC详解

JVMJava虚拟机组成详解