15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > 【Java】Java 虚拟机

【Java】Java 虚拟机

时间:2023-07-15 02:57:01 | 来源:网站运营

时间:2023-07-15 02:57:01 来源:网站运营

【Java】Java 虚拟机: 最近做面试题发现 Java 虚拟机还是考得挺多的。

1.运行时数据区域

JDK 1.6 运行时数据区域如下图:

程序计数器:记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。

Java 虚拟机栈:每个 Java 方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、常量池引用等信息。

本地方法栈:与 Java 虚拟机栈类似,区别是本地方法栈为本地方法服务。本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的。

:所有对象都在这里分配内存,是垃圾收集的主要区域("GC 堆")。

方法区:用于存放已被加载的信息、常量静态变量、即时编译器编译后的代码等数据。从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。

运行时常量池:方法区的一部分。Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。

直接内存:在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。

2.垃圾收集

垃圾收集主要是针对堆和方法区进行。

如何判断对象是否可以回收:

  1. 引用计数法:引用计数为 0 的对象可被回收。Java 虚拟机不使用引用计数算法。
  2. 可达性分析算法:以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。
  3. 方法区的回收:方法区主要存放永久代对象,主要是对常量池的回收和对类的卸载。
  4. finalize()
垃圾收集算法

  1. 标记-清除:标记:活动对象会在对象头部打上标记。清除:进行对象回收并取消标志位。
  2. 标记-整理:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
  3. 复制:将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。现在的商业虚拟机都采用这种收集算法回收新生代,但是并不是划分为大小相等的两块,而是一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。
  4. 分代收集:根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。一般将堆分为新生代和老年代。新生代使用:复制算法。老年代使用:标记 - 清除 或者 标记 - 整理 算法
CMS 收集器和 G1 收集器比较

CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。

分为以下四个流程:

在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。

具有以下缺点:

G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。

堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。

G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。

每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。

如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤:

具备如下特点:

3.内存分配与回收

3.1 内存分配策略

  1. 对象优先在 Eden 分配,当 Eden 空间不够时,发起 Minor GC。
  2. 大对象直接进入老年代,最典型的大对象是那种很长的字符串以及数组。
  3. 长期存活的对象进入老年代。
  4. 动态对象年龄判定:如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。
  5. 空间分配担保:在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。

3.2 内存回收

Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。

Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。触发条件:

4.类加载

包括以下 7 个阶段:

4.1 类加载器分类

从 Java 虚拟机的角度来讲,只存在以下两种不同的类加载器:

从 Java 开发人员的角度看,类加载器可以划分得更细致一些:

4.2 双亲委派模型

  1. 工作过程:一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。
  2. 好处:使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。

关键词:虚拟

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭