写点什么

架构师训练营—第九周作业

用户头像
Geek_shu1988
关注
发布于: 2020 年 11 月 22 日

请简述 JVM 垃圾回收原理。


Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制。JVM 实现了 Java 语言最重要的特征:即平台无关性。原理:编译后的 Java 程序指令并不直接在硬件系统的 CPU 上执行,而是由 JVM 执行。JVM 屏蔽了与具体平台相关的信息,使 Java 语言编译程序只需要生成在 JVM 上运行的目标字节码(.class),就可以在多种平台上不加修改地运行。Java 虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。因此实现 java 平台无关性。它是 Java 程序能在多平台间进行无缝移植的可靠保证,同时也是 Java 程序的安全检验引擎(还进行安全检查)。


JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area

classloader 把硬盘上的 class 文件加载到 JVM 中的运行时数据区域, 但是它不负责这个类文件能否执行,而这个是 执行引擎 负责的。


JVM 内存结构

JVM 内存结构通常来说由虚拟机栈、堆、本地方法栈、方法区、程序计数器组成,其中每个组件各司其职。



方法区(Method Area)


     方法区中主要存储了 Java 代码中的类加载的相关信息,比如类的名称、修饰符、构造器、静态方法、定义为 final 的常量、勒种的方法信息。这是一个面向全局线程共享的区域。


虚拟机栈(VM Stack)


     这一区域主要存放了大量的线程相关信息,它是随着虚拟机线程的诞生而诞生的,所以它是独属于线程私有的一块区域,其中存放了 Java 八大基本类型的变量,还有一些局部变量,也就是方法内部的变量。


本地方法栈(Method Area)


     这里存放了 Java 代码中调用的本地方法的信息,主要用于 native 方法。


堆(Heap)


     堆是 Jvm 立马很重要的一个区域,也是跟方法区一样属于全部线程都可以共享访问的一个区域,在堆中存储了大量的 Java 对象信息,但凡是 new 出来的对象都存放在这里,并且可以通过 jvm 提供的-Xmx 和-Xms 来调节堆的大小。其中-Xmx 是堆的最大值是多少,-Xms 是堆的最小内存,如果两者一致,则这个 Jvm 的堆大小就不能弹性伸缩。


堆内部也划分除了多个区域,分别是新生代、老年代以及持久代。这其中新生代又分为 Eden 和 Survivor01、Survivor02 等等。为什么要这么划分,这就涉及到了 Jvm 的 GC 回收策略了。


     新生代中存储了所有新创建的对象,一个新对象创建 OK 首先是存放在 Eden 区域的,当 Eden 区域快要被填满时,就会自动触发 Jvm 的 GC 机制,GC 会回收一些空闲的对象,再将幸存下来的其他对象转移到 Survivor01 中去,同样,如果 01 也满了 再次对 01 这一区域进行 GC 回收,将幸存者放入 Survivor02 区域中去。一旦在 Survivor02 中历经磨难多次存活超过 15 次,就会将其转移至老年代中去。至于持久代中,则主要存储一些常量池、方法区等数据。相关参数如下:


-Xmx:最大堆内存,如:-Xmx512m-Xms:初始时堆内存,如:-Xms256m-XX:MaxNewSize:最大年轻区内存-XX:NewSize:初始时年轻区内存.通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%-XX:MaxPermSize:最大持久带内存-XX:PermSize:初始时持久带内存-XX:+PrintGCDetails。打印 GC 信息 -XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的 1/3,老年代占 2/3


-XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10

Jvm GC 回收策略


JvmGC 经过这么长时间的发展,逐渐划分了以下几个垃圾回收策略,他们分别是:


复制回收算法


    此种方法通过依次扫描区域所有的可达对象,然后将其复制到另外一片区域保存起来,再将其现在正在使用的区域内存全部清空,此方法的优点在于方便快捷,只需要便利出所有的可达对象即可,而且不会出现碎片化内存。但是缺点也很明显,复制对象需要计算成本,此外需要准备一个额外相同 Eden 区域大小的内存空间,也是一笔巨大的开销。


标记清除法


    这种方法首先遍历整个区域中的对象,然后标记所有的可达对象,再将所有内存中未被标记的对象全部清除。主要缺点在于会产生大量的碎片内存。


标记整理法


    这种方法集上面两种算法的优点于一身,首先遍历整个空间对可达对象进行标记,然后再讲所有可达对象整理到一起去,最后清除掉不可达的对象,达到 GC 回收清理内存的目的。


Jvm 如何检测对象是否有用?


引用计数法


    顾名思义,这种方法其实很简单,就是你 new 出来一个对象之后,之后每次对该对象做了引用,那么就将该对象+1,在 GC 时,只需要判断该对象对应的 count 是否为 0 就可以轻松判断出是否还存在引用关系。


    但是这种方法很不严谨,循环引用会使得其产生内存泄漏,永远无法释放这些资源。


根搜索算法


    这个算法其实也很简单,如下图所示,Jvm 会起一个后台守护进程来维护一个树结构,如果发生引用就在树上维护一条边,那么同样的,如果这个引用被释放了,那么这个类也就和这个树失去了链接,从根节点 GC Root 对其进行搜索也搜索不到,便可以判断其对象已经无人再引用,可以释放。



用户头像

Geek_shu1988

关注

还未添加个人签名 2020.02.02 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营—第九周作业