前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图解 | JVM的11个核心知识

图解 | JVM的11个核心知识

作者头像
田维常
发布2022-04-19 09:23:13
4911
发布2022-04-19 09:23:13
举报
文章被收录于专栏:Java后端技术栈cwnait

大家好,我是田哥

最近很多人都在面试,都在问如何快速复习JVM,本文就采用文字+图的形式来解说JVM,希望对你有所帮助。

第1组:JDKJREJVM的关系

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

范围关系:JDK>JRE>JVM

具体见下图:

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

这两者的关系需要两张图才能说明白:

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

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

第4组:类加载器关系

一张图来说明:

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

直接上图:

栈指向堆

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

代码语言:javascript
复制
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。
代码语言:javascript
复制
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(因为不涉年代不在图中)

好了,本文就分享到这里,希望能帮助你更好的理解JVM核心知识。

技术栈:Spring Boot+Dubbo+Zookeeper+Redis+RabbitMQ+MyBatis+MySQL,第一版已经接近尾声,接下就是把这个项目文档化,需求、系统设计、表结构设计、技术难点等。分布式锁、分布式缓存、分布式session、分库分表等在项目中的应用。

然后,可以拿着这个项目进行改造,或重构成Spring Cloud(我后面也会重构的)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java后端技术全栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第1组:JDK、JRE、JVM的关系
  • 第2组:.java文件与.class文件的关系
  • 第3组:class文件与JVM的关系
  • 第4组:类加载器关系
  • 第5组:方法区、堆、栈之间到底有什么关系
    • 栈指向堆
      • 方法区指向堆
        • 堆指向方法区
        • 第6组:Minor、Major、Full GC的关系
        • 第7组:Survivor与Eden的关系
        • 第8组:引用计数法和可达性分享算法的关系
          • 引用计数法
            • 可达性分析算法
            • 第9组:对象的引用类型的关系
            • 第10组:垃圾回收算法的关系
              • 标记-清除算法
                • 复制算法
                  • 标记整理算法
                  • 第11组:垃圾收集器之间有什么关系
                  相关产品与服务
                  云数据库 MySQL
                  腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档