JVM知识点汇总

作者: 码上就说 | 来源:发表于2019-02-02 10:23 被阅读114次

    1.垃圾回收机制

    1.1 标记 - 清除算法
    1.2 标记 - 整理算法
    1.3 复制算法
    1.4 分代收集算法zxc

    2.JVM内存布局
    3.Java类加载

    1.垃圾回收机制

    GC的主要目的是清楚不再使用的对象,自动释放内存。

    • GC如何判断对象是否可以被回收?
      JVM引入了GC Roots,如果一个对象与GC Roots之间没有直接或间接的引用关系,例如某个失去引用的对象,或者两个互相环岛状循环引用的对象,这些对象是可以被回收的。
    • 什么对象可以作为GC Roots?
      类静态属性中引用的对象、常量引用的对象、虚拟机栈中引用的对象、本地方法栈中引用的对象等。
    1.1 标记 - 清除算法

    该算法会从每个GC Roots出发,依次标记有引用关系的对象,最后将没有被标记的对象清除,(或者标记处需要回收的对象,在标记完成后统一回收所有被标记的对象。)
    标记 - 清除算法是最基本的收集算法,因为后续的收集算法都是基于这种思路并对其不足进行改进的。


    mark-sweep.jpg

    不足之处:
    1.标记和清除两个过程的效率不高。
    2.标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序运行过程中需要分配较大的对象时无法得到足够大的内存空间。

    1.2 标记 - 整理算法

    首先从GC Roots出发标记存活的对象,然后将存活对象整理到内存空间的一端,形成连续的已使用空间,最后把已使用空间之外的部分全部清理掉。这样就不会产生空间碎片问题。


    mark.jpg
    1.3 复制算法

    为了可以并行地标记和整理,将空间氛围两块,每次只激活其中一块,垃圾回收时只需把存活的对象复制到另一块未激活的空间上,将未激活的空间标记为已激活,将已激活的空间标记为未激活,然后清除原空间中的原对象。

    特点:每次都对半区进行内存回收,内存分配不用考虑碎片的问题。
    只需要移动堆顶指针,按顺序分配内存,实现简单,运行高效。但是代价是将内存缩小为原来的一半,代价太大。


    解决内存浪费严重的问题:

    将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一个Survivor,当回收时,将Eden和Survivor中还活着的对象一次性地复制到另一块Survivor空间上,最后清理掉Eden和刚才才使用的Survivor空间。一般默认的Eden空间:Survivor空间是8:1,这样就有90%的空间。不会过分浪费了。


    copy.jpg
    1.4 分代收集算法

    根据对象的存活周期将内存划分为几块,把堆分为新生代和老年代,可以根据各个年代的特定采用最适当的收集算法。
    在新生代中每次GC都发现大批对象死去,少量存活,选用复制算法。
    老年对象存活率高,没有额外的空间担保,所以使用“标记 - 清理”或者“标记 - 整理”算法进行回收。

    2.JVM内存布局

    jvm-mem.jpg
    • 1.Heap(堆区):存储所有的实例对象,堆由垃圾回收器自己回收,堆区由子线程共享使用。


      classloader.jpg
    • 2.Metaspace(元空间):
    • 3.JVM Stack(虚拟机栈):JVM是基于栈结构的运行环境,栈结构移植性更好,可控性更强。栈帧在整个JVM体系中地位很高,包括局部变量表、操作栈、动态链接、方法返回地址等。
    • 4.Native Method Stacks(本地方法栈):本地方法栈在JVM内存布局中,也是线程对象私有的
    • 5.Program Counter Register(程序计数寄存器):寄存器存储指令相关的现场信息,PCR用来存放执行指令的偏移量和行号指示器等,线程执行和恢复都要依赖PCR

    从线程共享的角度来看,堆和元空间是所有线程共享的,而虚拟机栈、本地方法栈、程序计数器是线程内部私有的。下面看下Java内存结构。


    java-mem-thread.jpg

    3.Java类加载

    任何程序都需要加载到内存才能与CPU进行交互。Java中编译生成的.class文件也需要加载到内存中,才可以实例化。
    ClassLoader:提前加载.class类文件到内存中。
    Parents Delegation Model:双亲委派模型,是加载类时所用的模型。

    • 1.Load阶段读取类文件产生二进制流,并转化为特定的数据结构。
    • 2.Link阶段包括验证、准备、解析三个步骤
    • 3.Init阶段执行类构造器<clinit>方法,如果赋值运算是通过其他类的静态方法来完成的,那么会马上解析另外一个类,在虚拟机栈中执行完毕后通过返回值进行赋值。
    parent_delegate.jpg
    • Bootstrap是类加载中最高的一层,在JVM启动时创建,是最根基的类加载器。负责装载最核心的Java类,例如Object、String、System
    • Platform ClassLoader:平台加载器可以用来加载一些扩展的系统类,JDK9之前命名是Extension ClassLoader
    • Application ClassLoader:应用类加载器,主要加载用户定义的CLASSPATH路径下的类。

    低层次的类加载器不能覆盖更高层级类加载器已经加载的类,低层次类加载器想加载类,需要逐级向上询问这个类是否被加载,只有所有高层次的类都没有加载,才可以让当前类加载器加载这个未知类。

    相关文章

      网友评论

        本文标题:JVM知识点汇总

        本文链接:https://www.haomeiwen.com/subject/fqjdsqtx.html