运行时数据区

JVM的运行时数据区主要分为虚拟机栈、堆、本地方法栈、程序计数器和MetaSpace

JVM.png

虚拟机栈

用于存储局部变量。主要是基本数据类型和对象引用(地址)。每个方法都会开辟一个栈,栈随着方法的执行结束就出栈,线程私有

堆用于存放对象实例。线程公有

MetaSpace

用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据。线程公有

本地方法栈

用于存储虚拟机使用到的native方法的信息

程序计数器

当前线程所执行的行号

类加载过程

例如如下代码

  1. public class MainTest {
  2. public static void main(String[] args) {
  3. A a = new A();
  4. B b = new B();
  5. a.b = b;
  6. }
  7. }
public class B {}
public class A {
    public static final String CON = "my_local_test";

    public static StringBuffer sb = new StringBuffer("str");

    public B b;
}

在main方法需要使用到A对象时,会先查看MetaSpace区中该类是否已经被加载过了,如果没有被加载过就需要先加载。加载过程分为验证、准备、解析和初始化
验证:验证class文件是否符合JVM规范
准备:给对象申请内存,给对象设置初始值(例如:0或null)
解析:将符号引用变为直接引用
初始化:如果父类没初始化会先初始化父类。主要是给变量赋值
JVM-类加载过程.png

JVM-内存分布.png

GC算法

标记-清除算法

标记-清除算法就是将可回收的对象直接进行回收。会造成碎片,可能导致无法分配对象

标记-整理算法

标记-整理算法首先将可回收对象进行回收,回收后将存活对象向一端移动,一般用于老年代回收
image.png

复制算法

  • 复制算法将内存分为3块区域:edens0s1,默认比例为8:1:1
  • 对象默认在eden区进行创建
  • 当第一次GC时将eden区中存活的对象放入s0
  • 第二次时将edens0中的存活对象放入s1
  • 第三次将edens1中的存活对象放入s0
  • s0s1空间不够时由老年代进行过空间分配担保
  • 该算法一般用于新生代回收

image.png

垃圾收集器

image.png

Serial收集器

  • 单线程收集器,在进行垃圾收集时必须暂停所有其他工作线程,直到它收集结束
  • 新生代:复制算法,暂停所有用户线程
  • 使用参数:-XX:+UseSerialGC

    Serial Old收集器

  • Serial收集器的老年代版本

  • 老年代:标记-整理算法,暂停所有用户线程
  • 使用参数:-XX:+UseSerialGC

    ParNew收集器

  • Serial收集器的多线程版本

  • 新生代:复制算法,多线程垃圾收集,暂停所有用户线程
  • 使用参数:-XX:+UseParNewGC

    Parallel Scavenge收集器

  • 该收集器的目标是达到一个可控制的吞吐量,高吞吐量可以高效利用CPU时间,尽快完成任务,适合后台运算而不需要太多交互的任务。

  • 吞吐量=运行用户代码时间/(运行用户代码时间 + 垃圾收集时间),虚拟机运行了100分钟,垃圾收集花费1分钟,吞吐量为99%
  • 新生代:复制算法,多线程垃圾收集,暂停所有用户线程
  • 使用参数:-XX:+UseParallelGC
  • -XX:MaxGCPauseMillis:大于0的毫秒数,收集器尽可能保证回收花费的时间不超过设定值
  • -XX:GCTimeRatio:大于0小于100的整数,就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5%(即1/(1+19)),默认值为99,就是允许最大1%(即1/(1+99))的垃圾收集时间
  • -XX:+UseAdaptiveSizePolicy:当这个参数打开之后,就不需要手工指定新生代的大小(-Xmn)、
  • Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:
  • PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信
  • 息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC
  • 自适应的调节策略

    Parallel Old收集器

  • Parallel Scavenge收集器的老年代版本

  • 老年代:标记-整理算法
  • 使用参数:-XX:+UseParallelOldGC

    CMS收集器

  • 该收集器是一种以获取最短回收停顿时间为目标的收集器

  • 该收集器收集分为4个步骤:初始标记(Stop The World),并发标记,重新标记(Stop The World),并发清除
  • 老年代:标记-清除算法
  • 使用参数:-XX:+UseConcMarkSweepGC
  • 缺点:对CPU资源敏感,无法处理浮动垃圾(并发清除时用户线程还在运行产生新的垃圾),基于标记-清除进行收集(产生内存碎片)
  • -XX:+UseCMSCompactAtFullCollection:在顶不住需要FullGC时进行内存碎片合并(默认开启)
  • -XX:CMSFullGCsBeforeCompaction:设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入FullGC时都进行碎片整理)

    G1收集器

    G1收集器是将堆划分为一个个大小相等的region,每个region有可能是eden、survivor、old或humongous。Humongous用于存放大于等于1.5个region的数据
    G1收集器的收集过程分为:

  • 初始标记:仅仅标记一下GCRoots能直接关联到的对象,需STW

  • 并发标记:对堆中的对象进行可达性分析,这阶段耗时较长
  • 最终标记:修正并发标记阶段因用户程序运作而导致变动的那部分记录。
  • 筛选回收:对各个resion的回收价值和成本排序,根据用户所期望的GC停顿时间来制定回收计划

JVM-G1.png

跨代引用

只要使用了分代回收的机制,就会有跨带引用问题,例如:一个老年代对象引用了一个新生代对象,所以老年代对象也需要作为GC Root,但是扫描整个老年代对象的话效率就比较低下
为了解决这个问题,HotSpot虚拟机使用了RemeberedSet来记录老年代对象对新生代对象的引用。当进行MinorGC的时候只需要扫描这个RememberedSet中对新生代的引用即可