关于java:年终总结11组关系带你看清JVM全貌

45次阅读

共计 4158 个字符,预计需要花费 11 分钟才能阅读完成。

关注“Java 后端技术全栈”

回复“面试”获取全套面试材料

废话少说,间接开整:

第 1 组:JDKJREJVM的关系

JDK中蕴含 JRE,也包含JDK,而JRE 也包含JDK

范畴关系:JDK>JRE>JVM

具体见下图:

第 2 组:.java文件与 .class 文件的关系

这两者的关系须要两张图能力说明确:

第 3 组:class 文件与 JVM 的关系

JVM 通过类加载机制,把 class 文件装载进 JVM 中,而后 JVM 解析 class 文件的内容,于是就有了类加载过中的链接、初始化等。

第 4 组:类加载器关系

一张图来阐明:

第 5 组:办法区、堆、栈之间到底有什么关系

间接上图:

栈指向堆

如果在栈帧中有一个变量,类型为援用类型,比方:

package com.tian.my_code.test;
public class JvmCodeDemo {public  Object testGC(){
        int op1 = 10;
        int op2 = 3;
        Object obj = new Object();
        Object result=obj;
        return result;
    }
}

这时候就是典型的栈中元素 obj 指向堆中的 Object 对象,result 的指向和 obj 的指向为同一个对象。

应用命令

javac -g:vars JvmCodeDemo.java

进行编译,而后再应用

javap -v JvmCodeDemo.class >log.txt

而后关上 log.txt 文件

办法区指向堆

办法区中会寄存动态变量,常量等数据。

如果是上面这种状况,就是典型的办法区中元素指向堆中的对象。

堆指向办法区

办法区中会蕴含类的信息,对象保留再堆中,创立一个对象的前提是有对应的类信息,这个类信息就在办法区中。

第 6 组:Minor、Major、Full GC 的关系

Minor GC:产生在年老代的 GC。

  • Minor GC 是指从年老代空间(包含 Eden 和 Survivor 区域)回收内存。当 JVM 无奈为一个新的对象调配空间时会触发 Minor GC,比方当 Eden 区满了。
  • Eden 区满了触发 MinorGC,这时会把 Eden 区存活的对象复制到 Survivor 区,当对象在 Survivor 区熬过肯定次数的 MinorGC 之后,就会降职到老年代(当然并不是所有的对象都是这样降职的到老年代的),当老年代满了,就会报 OutofMemory 异样。
  • 所有的 MinorGC 都会触发全世界的暂停(stop-the-world),进行应用程序的线程,不过这个过程十分短暂。

Major GC:产生在老年代的 GC。

  • Major GC 清理 Tenured 区(老年代)。

Full GC:新生代 + 老年代,比方 办法区引起年老代和老年代的回收。

第 7 组:Survivor 与 Eden 的关系

对于这两者,最重要的是要明确为什么须要 Survivor 区? 只有 Eden 不行吗?

如果没有 Survivor,Eden 区每进行一次 Minor GC , 并且没有年龄限度的话,存活的对象就会被送到老年代。这样一来,老年代很快被填满, 触发 Major GC(因为 Major GC 个别随同着 Minor GC,也能够看做触发了 Full GC)。老年代的内存空间远大于新生代,进行一次 Full GC 耗费的工夫比 Minor GC 长得多。

执行工夫长有什么害处?

频发的 Full GC 耗费的工夫很长, 会影响大型程序的执行和响应速度。

可能你会说,那就对老年代的空间进行减少或者较少咯。

如果减少老年代空间,更多存活对象能力填满老年代。尽管升高 Full GC 频率,然而随着老年代空间加大, 一旦产生 Full GC, 执行所须要的工夫更长。

如果缩小老年代空间,尽管 Full GC 所需工夫缩小,然而老年代很快被存活对象填满,Full GC 频率减少。

所以 Survivor 的存在意义,就是缩小被送到老年代的对象, 进而缩小 Full GC 的产生,Survivor 的预筛选保障, 只有经验 16 次 Minor GC 还能在新生代中存活的对象, 才会被送到老年代。

第 8 组:援用计数法和可达性分享算法的关系

援用计数法

给对象增加一个援用计数器,每当一个中央援用它 object 时技术加 1,援用失去当前就减 1,计数为 0 阐明不再援用

  • 长处:实现简略,断定效率高
  • 毛病:无奈解决对象互相循环援用的问题,对象 A 中援用了对象 B,对象 B 中援用对象 A。
public class A {public B b;}
public class B {public C c;}
public class C {public A a;}
public class Test{private void test(){A a = new A();
        B b = new B();
        C c = new C();
        
        a.b=b;
        b.c=c;
        c.a=a;
    }
}

可达性剖析算法

当一个对象到 GC Roots 没有援用链相连,即就是 GC Roots 到这个对象不可达时,证实对象不可用。

GC Roots品种:

Java 线程中,以后所有正在被调用的办法的援用类型参数、局部变量、长期值等。也就是与咱们栈帧相干的各种援用。所有以后被加载的 Java 类。Java 类的援用类型动态变量。运行时常量池里的援用类型常量(String 或 Class 类型)。JVM 外部数据结构的一些援用,比方 sun.jvm.hotspot.memory.Universe 类。用于同步的监控对象,比方调用了对象的 wait() 办法。

第 9 组:对象的援用类型的关系

  • 强援用:User user=new User();咱们开发中应用最多的对象援用形式。

    特点:咱们平时典型编码 Object obj = new Object() 中的 obj 就是强援用。

    通过关键字 new 创立的对象所关联的援用就是强援用。

    JVM 内存空间有余,JVM宁愿抛出 OutOfMemoryError 运行时谬误(OOM),使程序异样终止,也不会靠随便回收具备强援用的“存活”对象来解决内存不足的问题。

    对于一个一般的对象,如果没有其余的援用关系,只有超过了援用的作用域或者显式地将相应(强)援用赋值为 null,就是能够被垃圾收集的了,具体回收机会还是要看垃圾收集策略。

  • 软援用:SoftReference<Object> object=new SoftReference<Object>(new Object());

    特点:软援用通过 SoftReference 类实现。软援用的生命周期比强援用短一些。只有当 JVM 认为内存不足时,才会去试图回收软援用指向的对象:即 JVM 会确保在抛出 OutOfMemoryError 之前,清理软援用指向的对象。软援用能够和一个援用队列(ReferenceQueue)联结应用,如果软援用所援用的对象被垃圾回收器回收,Java 虚拟机就会把这个软援用退出到与之关联的援用队列中。后续,咱们能够调用 ReferenceQueue 的 poll()办法来查看是否有它所关怀的对象被回收。如果队列为空,将返回一个 null, 否则该办法返回队列中后面的一个 Reference 对象。

    利用场景:软援用通常用来实现内存敏感的缓存。如果还有闲暇内存,就能够临时保留缓存,当内存不足时清理掉,这样就保障了应用缓存的同时,不会耗尽内存

  • 弱援用:WeakReference<Object> object=new WeakReference<Object> (new Object();ThreadLocal中有应用.

    弱援用通过 WeakReference 类实现。弱援用的生命周期比软援用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具备弱援用的对象,不论以后内存空间足够与否,都会回收它的内存。因为垃圾回收器是一个优先级很低的线程,因而不肯定会很快回收弱援用的对象。

    弱援用能够和一个援用队列(ReferenceQueue)联结应用,如果弱援用所援用的对象被垃圾回收,Java 虚拟机就会把这个弱援用退出到与之关联的援用队列中。利用场景:弱利用同样可用于内存敏感的缓存。

  • 虚援用:简直没见过应用,ReferenceQueue、PhantomReference

第 10 组:垃圾回收算法的关系

标记 - 革除算法

第一步:就是找出沉闷的对象。咱们反复强调 GC 过程是逆向的,依据 GC Roots 遍历所有的可达对象,这个过程,就叫作标记。

第二部:除了下面标记进去的对象以外,其余的都分明掉。

  • 毛病:标记和革除效率不高,标记和革除之后会产生大量不间断的内存碎片

复制算法

新生代应用,新生代分中Eden:S0:S1= 8:1:1,其中前面的 1:1 就是用来复制的。

当其中一块内存应用完了,就将还存活的对象复制到另外一块下面,而后把曾经应用过的内存空间一次 革除掉。

个别对象调配都是进入新生代的 eden 区,如果 Minor GC 还存活则进入 S0 区,S0S1 一直对象进行复制。对象存活年龄最大默认是 15,大对象进来可能因为新生代不存在间断空间,所以会间接接入老年代。任何应用都有新生代的 10% 是空着的。

  • 毛病:对象存活率高时,复制效率会较低,节约内存。

标记整顿算法

它的次要思路,就是挪动所有存活的对象,且依照内存地址程序顺次排列,而后将末端内存地址当前的内存全副回收。然而须要留神,这只是一个现实状态。对象的援用关系个别都是非常复杂的,咱们这里不对具体的算法进行形容。咱们只须要理解,从效率上来说,个别整顿算法是要低于复制算法的。这个算法是躲避了内存碎片和内存节约。

让所有存活的对象都向一端挪动,而后间接清理掉端边界以外的内存。

从下面的三个算法来看,其实没有相对最好的回收算法,只有最适宜的算法。

第 11 组:垃圾收集器之间有什么关系

「新生代收集器」:Serial、ParNewParallel Scavenge

「老年代收集器」CMS、Serial Old、Parallel Old

「整堆收集器」G1ZGC(因为不涉年代不在图中)

举荐浏览

《算法图解》.pdf

田哥:面试被问 == 与 equals 的区别,该怎么答复?

《一本小小的 MyBatis 源码剖析书》.pdf

正文完
 0