JVM概览

来源:互联网 发布:南昌软件测试培训 编辑:程序博客网 时间:2024/06/05 16:05

内建的JIT编译器日渐成熟的垃圾收集器不断改进的运行时环境(JVM Runtime Environment),使得Java应用的性能都能达到要求。但是许多Java技术人员只把JVM看成黑盒,若要提高Java性能调优的能力,必须对现代JVM有些基本认识。
HotSpot Java虚拟机有3个主要组件:VM运行时(Runtime)、JIT编译器(JIT Compiler)以及内存管理器(Memory Manager)。

HotSpot VM的基本架构

这里写图片描述

  • JIT 编译器和垃圾收集器(Serial、Throughput、 CMS、 G1)是可插拔的。
  • VM运行时系统为HotSpot JIT编译器和垃圾收集器提供服务和通用API。此外,它还为VM提供启动、线程管理、JNI等基本功能。
  • 64位VM oops(Ordinary Object Pointers 普通对象指针)的长度从32位变成64位,使得其CPU缓存的效率降低。HotSpot VM通过压缩指针(–XX:+UseCompressedOops)来提升性能。

HotSpot VM运行时

HotSpot VM运行时环境担当相当多的责任,包括命令行解析、VM生命周期管理、类加载、字节码解释、异常处理、同步、线程管理、Java本地接口、VM致命错误处理和C++堆管理。

命令行选项

  • 标准选项 ,所有Java虚拟机都必须实现的选项
  • 非标准选项,不保证、也不强调所有JVM实现都必须支持,以-X为前缀
  • 非稳定选项,为了特定需要而对JVM的运行进行校正,并且可能需要有系统配置参数的访问权限, 以-XX为前缀。
  • 对于带有布尔标记的非稳定选项来说,选项名前的+或-表示true或者false。

VM类加载

  • 加载; Java API如Class.forName()、ClassLoader.loadClass()、反射API和JNI_FindClass都可以引发类加载。对于给定的Java类或接口,类加载时会依据它的名字找到Java类二进制类文件,定义Java类,然后创建代表这个类或者接口的java.lang.Class对象。如果找不到二进制表示,就会抛出NoClassDefFound
  • 链接
    • 验证;检查类文件的语义、常量池符号以及类型。如果有错,就会抛出VerifyError
    • 准备;创建静态字段,初始化为标准默认值,以及分配方法表,此时还没有执行任何Java代码。
    • 解析符号引用
  • 初始化类;运行类构造器
  • 处于性能优化的考虑,通常直到类初始化时,HotSpot VM才会加载和链接类。

类加载器委派

  • 类加载器之间是层级关系,每个类加载器都可以为派给上一级类加载器。
  • 类加载器的层级查找顺序为启动类加载器扩展类加载器系统类加载器(应用程序类加载器)
  • Java的类型由全限定名和类加载器唯一确定,类加载器定义了命名空间
  • 需要用一篇文章详细解释

解释器

  • 基于模板,TemplateTable包含与每个字节码对应的机器代码,每个模板描述一个字节码。
  • JVM持续动态监控热点,通过JIT,动态生成机器码(JIT编译代码),集中优化这些热点。

同步

同样需要一篇文正详细解释

  • 使用线程实现并发
  • 使用monitor对象保障线程运行代码之间的互斥。monitor可以锁定或者解锁,但任何时刻只能有一个线程拥有该monitor对象

Java Monitor 从两个方面来支持线程之间的同步,即:互斥执行与协作Java 使用对象锁 ( 使用 synchronized 获得对象锁 ) 保证工作在共享的数据集上的线程互斥执行 , 使用 notify/notifyAll/wait 方法来协同不同线程之间的工作。这些方法在 Object 类上被定义,会被所有的 Java 对象自动继承。
实质上,Java 的 Object 类本身就是监视者对象,Java 语言对于这样一个典型并发设计模式做了内建的支持。下图很好地描述了 Java Monitor 的工作机理。
这里写图片描述

  • 偏向锁,其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking

线程管理

  • 线程模型:Java线程(java.lang.Thread实例)被一对一映射为本地操作系统线程
  • 线程状态
    • 新线程
    • 线程在Java中:线程正在执行Java代码
    • 线程在VM中:线程正在HotSpot VM中执行
    • 线程阻塞:线程因某种原因而被阻塞
  • 安全点:这么一种状态,所有的Java执行线程都会被阻塞,任何执行本地代码的线程都不能返回Java代码。比如GC 的Stop-The-World阶段是安全点操作。

HotSpot VM垃圾收集器

几个特点:

  • 分代垃圾收集;HotSpot VM将堆分成两个物理区:新生代(Minor GC)、老年代(Major GC)。永久代虽然称为代,但是实际上不应该看作分代层次的一部分。VM只是用它来存储元数据,例如类的数据结构、保留字符串(Interned String)等。
    这里写图片描述

这里写图片描述

  • 垃圾收集器使用称为卡表(Card Table)的数据结构,使其不用扫描整个老年代就能识别新生代中的存活对象,从而缩短Minor GC的时间。

这里写图片描述

  • Minor GC,Eden中的存活对象被复制到未使用的Survivor。被占用Survivor里不够老的存活对象也被复制到未使用的Survivor。最后Survivor里足够老的存活对象被提升到老年代。假如Survivor不足以容纳Eden和另一个Survivor中的存活对象,称作Survivor存活对象溢出,多余的对象将被移到老年代,这成为过早提升。应该调优尽量避免。

这里写图片描述

  • 快速内存分配。大部分新生对象都是在Eden上(想想为什么),因为Minor GC以复制方式回收新生代,其好处在于回收以后Eden总为空,在Eden中运用被称为指针碰撞的技术就可以有效分配空间。这种技术追踪最后一个分配的对象(称为top),当新的分配请求时,分配器只需要检查top和Eden末端之间的空间是否能够容纳。如果能够容纳,top跳到新近分配对象的末端。而且VM采用线程本地分配缓冲区(TLAB),为每个线程设置各自的缓冲区,避免锁的使用。

垃圾收集器

这里写图片描述
分为三类:

  • Serial收集器

    年轻代采用复制算法,即 Eden + From –> To + 老年代
    老年代采用滑动压缩标记-清除算法(Sliding Compacting Mark-Sweep),也称为**标记-压缩(Mark-Compa
    ct)**垃圾收集器。
    Serial收集器适合大多数对停顿时间要求不高和在客户端运行的应用。
    Stop-The-World

  • Parallel 收集器

    HotSpot VM中能够并行的垃圾收集器包括Parallel Scavenge 收集器(年轻代)、ParNew收集器(年轻代)和Parallel Old收集器。
    Throughput收集器通常特指Parallel Scavenge收集器
    可以把ParNew收集器和Parallel Old收集器认为是Serial的并行版本。老年代采用标记-压缩,年轻代采用复制算法。
    主要关注应用的吞吐量
    Stop-The-World

  • CMS

这里写图片描述

Mostly-Concurrent收集器,也称为并发标记清除收集器(Concurrent Mark-Sweep GC、CMS收集器)
老年代垃圾收集器,年轻代可以是Serial 或者 ParNew
与Parallel 收集器相比,CMS老年代停顿变短,但代价是新生代停顿略微拉长、吞吐量有所降低、堆的大小有所增长,并且由于并发,垃圾收集还会占用应用的CPU周期。

G1收集器将分单独章节解释。
需要思考的几个问题:
对象池化适用于什么情况?(Netty 大量使用)
堆外内存适用于什么情况?

1 0
原创粉丝点击