一份JVM的简单总结

Java源代码是如何运行在机器上的:
  • Java会首先通过编译将源文件转为.class文件,这是为了提高之后代码的运行效率以及安全,在这个过程中会对代码进行编译检查。
  • 现在将这个.class文件交给类加载器(类加载子系统),类加载子系统会将该字节码文件加载到虚拟机中,之后由虚拟机中的方法区存储这个文件的字节码指令。
  • 接下来执行引擎就可以拿到方法区里的字节码指令通过解释器将这些指令翻译为机器指令运行;如果某些指令为热点指令,还可由JIT编译器将其机器指令缓存起来,下次可直接执行。
  • 而将代码在执行的时候,产生的Java对象会被存到虚拟机的**堆里,**并且会把当前正在执行的某个方法的信息存到JVM的Java方法栈中。如果过程中执行了本地方法,也就是Native方法,那么这些本地方法的信息就会存储到JVM本地方法栈。
  • 代码在运行的过程中需要依赖JVM中的程序计数器,通过它可以知道每个线程下一条该执行哪一条指令。
  • 垃圾回收器会通过指定的算法查找到没有被引用的对象,并将他们从堆中清除。
JVM的组成部分
  • JVM由三个部分组成,分别是类加载子系统、运行时数据区、执行引擎,其中:

    • 运行时数据区包含方法区、堆、Java方法栈、本地方法栈、程序计数器
    • 执行引擎中包含解释器、JIT编译器、垃圾回收器
类加载子系统的加载步骤
  • 首先加载到class文件会对他进行校验,检查他的格式以及是否被恶意篡改等。
  • 为其中的静态变量分配内存空间并赋予一个初始值
  • 为引用类型的变量将其引用的符号解析为引用地址
  • 为静态变量赋予代码中指定的初始值
类加载器是如何进行类加载的
  • 有两种类加载器:引导类加载、自定义类加载器(平台类加载器、系统类加载器)
  • 引导类加载器用来加载JDK平台大量内部模块中的类
  • 自定义加载器通常用来加载类路径target/classes和某些路径上的模块中的应用类
  • 类加载器的加载是使用双亲委派机制。当去调用一个类加载器去加载类时,首先会去调用他的上一级加载器,如果上一级加载器还有父加载器,那么还会请求他的父加载器去加载,若该类尚未被加载并且父加载器也无法加载则会调用最初的类加载器去加载该类。双亲委派机制可以避免类被重复加载、防止核心代码被修改引发安全问题。
关于tomcat的自定义类加载器
  • tomcat是一个web服务器,但首先它是一个Java程序,这就表示他有一个main程序入口,同时它只有一个系统类加载器。这就出现了一个问题,假如说tomcat中现在有两个web应用,并且这两个应用都有相同的完整类名,由于类无法重复被加载,这就导致了第二个应用中的类就无法被加载了。
  • tomcat的解决办法是为每个应用分别设置各自的类加载器WebappClassLoader,这样就可以分别加载不同应用中的类,不会发生冲突。
详细的运行时数据区
  • 方法区(线程共享)

    • 常量池
    • 类信息
    • 方法信息
  • 堆(线程共享)

    • 新生代

      • Eden 伊甸区

        • 新对象会先放到Eden区,如果对象太大,YGC后无法放到S0,就会直接被放到老年代。如果对象的大小超过了它,这直接进到老年代。这个区域占新生代 8/10,比例可调整。
        • 当这个区域的内存被占满了之后,YGC会将没有被引用的垃圾对象回收掉,然后将存活的对象存放到S0并且记录一次GC次数
      • S0

        • 存放YGC后仍存在的对象,这个区域占新生代 1/10,比例可通过配置参数调整
        • 如果这里有垃圾对象,YGC就会将这些垃圾对象回收掉,然后将存活的对象放到S1并且加一次GC次数。若GC次数达到15次依然没有被回收,那么下次YGC时,这些对象就会被直接放到老年代
      • S1

        • 存放YGC后仍存在的对象,这个区域占新生代 1/10,比例可通过配置参数调整
        • 如果这里有垃圾对象,YGC就会将这些垃圾对象回收掉,然后将存活的对象放到S0并且加一次GC次数。若GC次数达到15次依然没有被回收,那么下次YGC时,这些对象就会被直接放到老年代
      • 新创建不久的对象会在新生代中,可以通过参数设置它与老年代各自在堆内存的空间占比

    • 老年代

      • 老年代中存放的是存活足够久的对象,可以通过参数设置它与新生代各自在堆内存的空间占比
      • 除了CMS垃圾收集器外,其他垃圾收集器都是在整个堆都要回收的时候才对老年代进行垃圾回收
    • 对象和数组都存放在堆中,通常会将堆的初始化大小设置的与堆最大内存大小相同,这样可以省去GC后JVM对堆内存的修改操作,可以提高效率

  • java方法栈(每个线程都有一个)

    • 栈帧(每个方法就是一个栈帧)

      • 局部变量表

        • 用来记录方法中局部变量的名字、位置
      • 操作数栈

        • 用来对执行字节码指令的计算操作,从局部变量表中拿数据计算并返回
      • 动态链接

      • 方法返回地址

    • 方法执行会入栈、执行完会出栈

    • 如果线程很多,导致没有足够的空间创建Java方法栈,就会出现内存溢出

    • 如果栈中的栈帧也就是要执行的方法过多,就会出现栈溢出

    • 可以设置方法栈的大小

  • 本地方法栈(每个线程都有一个)

  • 程序计数器(每个线程都有一个)

    • 程序计数器用来记录要执行的下一条指令的地址
    • 程序的逻辑控制功能要依赖它来完成,例如循环、if-else、异常等
    • 它不会出现内存溢出,因为虚拟机会为他分配足够的内存空间
怎么找到垃圾对象
  • 通过一个GC Root作为起点,然后一直去找它所引用的对象,被找到的对象就是被引用的,其他没有找到的就是垃圾对象。将这种查找方法称为可达性分析法。
  • GC Roots包含Java方法栈和本地方法栈中正在执行的方法中的参数、局部变量所引用的对象、方法区中的类中的静态、常量属性所引用的对象。
垃圾回收算法
  • 标记-清除算法(Mark-Sweep)

    • 这个算法通过两次查询堆内存空间来实现。第一次查询从GC Roots开始查询可达对象,也就是被引用的对象,然后标记这些对象是可达的;第二次通过循环将堆中没有被标记可达的对象回收。

    • 优点:

      • 简单
    • 缺点:

      • 线性遍历,效率不是很高
      • 会产生内存随便,可能会浪费内存空间
  • 复制算法

    • 这个算法将内存空间分为两块,运行时只会使用到其中的一块空间存储对象,而另一块空间则用来在垃圾回收时存储当前内存空间的可达对象,然后转为使用它。然后清除垃圾对象(其实没有清除,下次直接覆盖就可以了,不用在意有没有值,反正都是垃圾)

    • 优点

      • 不会出现内存碎片
      • 省去了标记和清除的操作,提高了效率
    • 缺点

      • 有很大一块空间是空闲的,浪费了资源
      • 对象在被存到另一个内存空间,对象的内存地址会改变,需要修改栈帧中属性记录的引用地址
      • 效率的高低取决于可达对象的数量,可达对象多,处理的次数就多,效率就低
  • 标记-整理算法(Mark-Compact)

    • 这个算法第一步与标记-清除算法一样,从GC Roots开始查询可达对象;然后整理这些可达对象为在一端是连续的;最后清理这些连续可达对象以外的内存空间

    • 优点

      • 不会出现内存碎片
      • 不需要额外的内存空间
    • 缺点

      • 效率低,需要标记和移动可达对象的位置
      • 有些可达对象的内存地址改变了,有些地方的属性的引用地址也需要修改,进一步拉低了效率
  • 复制算法适合可达对象少、垃圾对象多的情况。因 为新生代中的大部分对象存活时间短,所以这个区域适合复制算法

  • 标记-清除算法和标记-整理算法适合可达对象多,垃圾对象少的情况。因为老年代中的大部分对象都是可达对象,而垃圾对象较少,所以这个区域适合标记-清除算法

G1垃圾收集器
  • Java11使用G1垃圾收集器。逻辑上分代。这种算法会将堆空间分为一个个不同类型的region,有伊甸区类型、幸存者区类型、老年区类型以及存储达对象的Humongous区类型(如果一个对象>=region大小的1/2就是大对象)

  • 垃圾收集过程

    • STW(暂停用户线程)
    • 根据GCRoot找到可达对象,并做初始标记
    • 并发标记(与用户线程一起执行,找到额外的可达对象并标记)
    • STW(暂停用户线程)
    • 解决掉前面步骤可能会出现的标记误差,完成最终标记
    • STW(暂停用户线程)
    • 采用复制算法将region中的可达对象复制到相邻的空闲region中,然后回收这个region,默认执行时间为200ms,可以设置执行的时间,所以并不一定会回收所有的垃圾对象。一般将这一操作称为**筛选回收,**因为会优先回收那些垃圾对象比较多的region
  • 垃圾收集器的触发

    • YGC:Eden区满,对Eden区进行GC
    • MixedGC:当老年代的占用率达到了指定的百分比,就对新生代、老年代、Humongous区进行垃圾回收
    • FGC:在MixedGC的时候,如果复制算法所需的内存不够,就会触发FGC
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值