jvm 方面

  根据VM规范,VM应该被划分为五块区域——即VM栈、堆、方法区、程序计数器、本地方法栈五个部分。如下图所示(这里介绍的是JDK1.8 JVM运行时内存数据区域划分。1.8同1.7比,最大的差别就是:元数据区取代了永久代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存,具体实际例子:jdk1.4引入了NIO,它可以使用Native函数库直接分配堆外内存,或者使用nio.DirectByteBuffer ):---引入堆内存和堆外内存的使用,常规来讲GC的回收是针对堆内存的回收,通过jvm参数-Xms,-Xmx等设置堆的大小和最大值。
1590737548.png
  其中:栈:函数中定义的基本类型变量和引用变量都在栈内存,堆内存: new创建的对象,数组,以及对象的实例变量。

  Jdk1.8的堆的分配情况:Eden,Old, surviver ,默认分配规则:

    老年代 : 三分之二的堆空间

    年轻代 : 三分之一的堆空间
    *   eden区: 8/10 的年轻代空间
    *   survivor0 : 1/10 的年轻代空间
    *   survivor1 : 1/10 的年轻代空间

  命令行上执行如下命令,查看所有默认的jvm参数: java -XX:+PrintFlagsFinal -version

  通过命令: 默认2:8
image.png
  Eden:该区域是最主要的刚创建的对象的内存分配区域,绝大多数对象都会被创建到这里(除了部分大对象通过内存担保机制创建到Old区域,默认大对象都是能够存活较长时间的),该区域的对象大部分都是短时间都会死亡的,故垃圾回收器针对该部分主要采用标记整理算法了回收该区域。

  Surviver:该区域也是属于新生代的区域,该区域是将在Eden中未被清理的对象存放到该区域中,该区域分为两块区域,采用的是复制算法,每次只使用一块,Eden与Surviver区域的比例是8:1,是根据大量的业务运行总结出来的规律。

  Old:该区域是属于老年代,一般能够在Surviver中没有被清除出去的对象才会进入到这块区域,该区域主要是采用标记清除算法。

  java GC的字节码解码编译器:

   为了满足不同用户场景的需要,HotSpot 内置了多个即时编译器:C1、C2 和 Graal。Graal 是 Java 10 正式引入的实验性即时编译器,在专栏的第四部分我会详细介绍,这里暂不做讨论。之所以引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。C1 又叫做 Client 编译器,面向的是对启动性能有要求的客户端 GUI 程序,采用的优化手段相对简单,因此编译时间较短。

  C2 又叫做 Server 编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。

  从 Java 7 开始,HotSpot 默认采用分层编译的方式:热点方法首先会被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。

  为了不干扰应用的正常运行,HotSpot 的即时编译是放在额外的编译线程中进行的。HotSpot 会根据 CPU 的数量设置编译线程的数目,并且按 1:2 的比例配置给 C1 及 C2 编译器。

  在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。

java jvm的GC回收算法:

1、标记清除

  使用场景

  存活对象较多的情况下比较高效

  适用于年老代(即旧生代)

缺点:

   容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块儿大小但是小于其中两块儿的和),会提前触发垃圾回收

   扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)

  2、复制算法(现在的商业虚拟机都采用这种收集算法来回收新生代)

  使用场景

  存活对象较少的情况下比较高效,

  扫描了整个空间一次(标记存活对象并复制移动)

使用年轻代

缺点:

  需要一块空的内存空间

  需要复制移动对象

  前提:复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的

  3、标记整理

  标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。

    首先也需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。

  4、分代收集算法 :CMS 和G1

  jdk1.8的 G1垃圾回收

  在G1中,堆内存通常被分为几千个大小相同region。同样的,在ZGC中堆内存也被分成大量的区域,它们被称为page,不同的是,ZGC中page的大小是不同的。

  ZGC有3种不同的页面类型:小型(2MB大小),中型(32MB大小)和大型(2MB的倍数)。

  在小页面中分配小对象(最大256KB大小),在中间页面中分配中型对象(最多4MB)。大页面中分配大于4MB的对象。大页面只能存储一个对象,与小页面或中间页面相对应。

  有些令人困惑的大页面实际上可能小于中等页面(例如,对于大小为6MB的大对象)。

总结起来,G1的年轻代收集归纳如下:

*   堆就是一整块内存空间,被分为多个heap区(regions)。
*   年轻代内存由一组不连续的heap块也就是region组成. 这使得在需要时很容易进行容量调整。
*   年轻代的垃圾收集,或者叫 young GCs, 会发生stop the world。 在回收时所有的应用程序线程都会被暂停。
*   年轻代 GC 通过多线程并行进行。
*   存活的对象被拷贝到新的 survivor 块或者老年代。

老生代的G1垃圾回收有以下几个关键点:

  1、并发标记阶段(Concurrent Marking Phase)

*   活跃度信息在程序运行的时候就被并行的计算了出来。
*   活跃度(liveness)信息标记出哪些区域块最适合回收,在转移暂停期间最适合回收掉。
*   没有sweep阶段。但CMS是有这个阶段的。

2、重新标记阶段(Remark Phase)

*   使用了Snapshot-at-the-Beginning (SATB)算法,这个要比CMS的算法快很多。
*   完全空的区域块会被直接回收掉。

3、复制/清除阶段(Copying/Cleanup Phase)

*   年轻代和老年代会被同时回收。
*   老年代的区域块会不会被选择,取决于它的活跃度。

  垃圾回收有两种类型:Minor GC(对新生代进行回收,不会影响到年老代。因为新生代的 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。) 和 Full GC (也叫

  Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比MinorGC要慢,因此应该尽可能减少Full GC的次数,导致FullGC的原因包括:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用等)

你可能感兴趣的:(jvm 方面)