什么是 JVM
JVM 是 Java Virtual Machine 的缩写,翻译成中文就是 Java 虚拟机,简单点说就是一种能够运行 Java 字节码的虚拟机。JVM 有着自己一套完整的硬件架构,还具有相应的指令系统,它屏蔽了与具体操作系统平台相关的信息,让 Java 程序只需要生成在 Java 虚拟机上运行的字节码,就可以在多种平台上不加修改地运行。
Java 虚拟机不是只为 Java 而生,实际上只要编译能够生成正确的 Java 字节码文件,那么这个语言就能在 JVM 上运行。目前市面上就有很多这样的语言:
- Groovy
- Kotlin
- Scala
- JRuby
- Jython
- ……
Groovy、Kotlin、Scala 都是原生就能在 JVM 上实现运行的新语言,JRuby 和 Jython 则是移植到 JVM 的旧语言(实现了相应的 JVM 编译器)。下面这张图可供大家参考理解:
JVM 并不是一个具体的实现,或者说它只是一种规范,HotSpot 则是 Java 虚拟机的一个实现,它是 Oracle 官方发布并维护的,当然除了 HotSpot,还有 IBM J9、Kaffe、TaobaoVM 等等,不过我们绝大多数人都在使用 HotSpot,所以我们平时说的 JVM 调优,就是基于 HotSpot 的调优。
JVM 规范相关的知识可以参考 Oracle 官网(JDK 8):
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
JVM 规范随着 JDK 版本的迭代也在不断变更,目前我们绝大多数同学应该都还在使用 JDK 8,所以下面的内容都会以 JDK 8 和 HotSpot 为基础。
GC 基础概念
GC 就是 Garbage Collection 的缩写,翻译过来就是垃圾回收,学 Java 的同学都知道,我们不需要手动去管理内存的回收,都是交给 GC 来自动回收。但是正因为 GC 是自动回收机制,肯定没有手动回收那样精确、高效,并且还会产生一些问题需要我们自行解决,这也是我们需要掌握 GC 技术的原因。
如何识别垃圾对象
引用计数法
顾名思义,为每个对象添加一个引用计数器,当有其他对象引用该对象时计数器 +1,反之则 -1,一旦某个对象的引用计数器为 0,则表示该对象可以被回收了。
这种方法的优点在于它能够快速回收不再被使用的对象,同时在回收过程中不会导致长时间的停顿,还可以清晰地标明每一个对象的生存周期。对于一些实时性要求比较高或内存受限的系统,这种方式非常适合。
当然这种方法存在着两个主要缺点:
- 频繁更新引用计数会降低运行效率
- 无法解决循环引用的问题(可以通过其他算法辅助优化解决)
可达性分析
这种算法的根本思路:给定一个集合的引用作为根出发,通过引用关系遍历对象图,能被遍历到的(可到达的)对象就被判定为存活,其余对象(也就是没有被遍历到的)就自然被判定为死亡。该算法的本质是找到所有活的对象,从而把其他的认定为垃圾对象,而不是直接找出所有垃圾对象并回收。
回收算法
标记—清除(Mark-Sweep)
从 GC Root 开始遍历搜索并标记,然后将没有标记的对象直接回收,整个过程不会发送对象的移动。这种算法容易产生大量的空闲空间碎片,使得大容量对象不容易获得连续的内存空间,从而造成空间浪费。在存活对象较多的时候比较高效。
标记—整理(Mark-Compact)
该算法和第一个非常类似,主要目的就是避免内存碎片化的问题,它会在回收的同时将保留的对象搬运到连续的内存空间。
复制(Copying)
将内存空间分成两个部分,每次回收时将一个半区的存活对象复制到另外一个半区。复制算法可以通过碰撞指针的方式进行快速地分配内存,但是也存在着空间利用率不高的缺点。
标记—清除 | 标记—整理 | 复制 | |
---|---|---|---|
速度 | 中等 | 最慢 | 最快 |
空间开销 | 少(但会堆积碎片) | 少(不堆积碎片) | 通常需要活对象的 2 倍大小(不堆积碎片) |
是否移动对象 | 否 | 是 | 是 |
关于时间开销
网友评论