java技术栈导图

160 阅读7分钟

​​​这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。​

My Wealth.png

JAVA

1. JVM

1.1 线程共享

1.1.1 方法区
  • 类型信息:
    • JVM加载类型(类class、接口interface、枚举enum、注解annotation)的完整有效名称(包名+类名)、其直接父类的完整有效名称、类型的修饰符、其直接继承的接口列表。
  • 域(成员变量)信息
    • 类型的所有成员变量的相关信息以及成员变量的声明顺序。
  • 方法信息
    • 包括了类型的成员方法的名称、返回类型、参数列表、修饰符、字节码、操作数栈、局部变量表、异常表等。
  • 静态变量
    • non-final的静态类变量和全局常量。区别在于全局常量在编译器给指定值,静态类变量在加载时准备阶段赋初值,初始化阶段再给指定值。
  • JIT代码缓存
    • 即时编译产生的代码缓存,将热点代码编译成与本地平台相关的机器码,并保存到内存。默认需要10000次之后
  • 运行时常量池
    • 字面量
    • 符号引用
1.1.2 堆
  • Eden
  • survival
    • from
    • to
  • Old

1.2 线程私有

1.2.1 栈
  • 栈帧:
    • 局部变量表
      • 基本数据类型
      • returnAddress 类型
      • 对象引用
      • 非static方法0号位为this
    • 操作数栈 主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引 来访问,而是压栈和出栈的方式
    • 动态链接 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支 持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引 用。
    • 方法返回地址
1.2.2 本地方法栈

本地方法栈和虚拟机栈类似,只不过本地方法栈为 Native 方法服务。

1.2.3 程序计数器

内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指 令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区 域是唯一一个 java 虚拟机规范没有规定任何 OOM 情况的区域。

2. GC

2.1 GC触发方式

在JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚 拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被任何引用的对象,并将它 们添加到要回收的集合中,进行回收。

2.2 GC方式

2.2.1 Partial GC
  • Minor GC针对Eden区
  • Young GC针对Eden,s0,s1区
  • Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式
  • G1特有-Mixed GC
2.2.2 MajorGC

通常是跟full GC是等价的,收集整个GC堆。但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的full GC还是old GC。

2.2.3 FullGC

收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。
触发方式:

  • 调用System.gc时,系统建议执行Full GC,但是不必然执行
  • 老年代空间不足
  • 方法区空间不足
  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
  • 由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

2.3 收集器

2.3.1 新生代收集器
  • Serial
  • ParNew
  • Parallel Scavenge
2.3.2 老年代收集器
  • Serial Old
  • Parallel Old
  • CMS
2.3.3 整堆收集器
  • G1

2.4 对象存活判断方式

2.4.1 引用计数法

所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时, 就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对 象没有被引用,也就是“死对象”,将会被垃圾回收. 引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A,B 对象的引用计数器都不为零,也就造成无法完成垃圾回收, 所以主流的虚拟机都没有采用这种算法

2.4.2 引用链法

该算法的思想是:从一个被称为 GC Roots的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用。

可以作为 GC Roots 的对象:

  • 虚拟机栈中引用的对象
  • 方法区类静态属性引用的对象
  • 方法区常量池引用的对象
  • 本地方法栈 JNI 引用的对象

2.5 SafePoint

GC 的时候必须要等到 Java 线程都进入到 safepoint 的时候 VMThread 才能开始
执行 GC

2.6 收集算法

2.6.1 标记清除

这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回 收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高, 标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在分配较 大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作。

2.6.2 标记整理

该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时, 也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象 移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。

2.6.3 复制算法

为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使 用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一 次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的 代价太高,每次基本上都要浪费一般的内存。 于是将该算法进行了改进,内存区域不再是按照 1:1 去划分,而是将内存划分为 8:1:1 三部分,较大那份内存交 Eden 区,其余是两块较小的内存区叫 Survior 区。每次都 会优先使用 Eden 区,若 Eden 区满,就将对象复制到第二块内存区上,然后清除 Eden 区,如果此时存活的对象太多,以至于 Survivor 不够时,会将这些对象通过分配担 保机制复制到老年代中。(java 堆又分为新生代和老年代)

2.6.4 分代收集

现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代 和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这 时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所 以可以使用标记-整理 或者 标记-清除。