关于java:持续输出面试题之JVM性能调优

44次阅读

共计 3363 个字符,预计需要花费 9 分钟才能阅读完成。

什么是 JVM?

JVM 是 Java Virtual Machine(Java 虚拟机)的缩写,JVM 是一种用于计算设施的标准,它是一个虚构进去的计算机,是通过在理论的计算机上仿真模仿各种计算机性能来实现的。

Java 语言的一个十分重要的特点就是与平台的无关性。而应用 Java 虚拟机是实现这一特点的要害。个别的高级语言如果要在不同的平台上运行,至多须要编译成不同的指标代码。而引入 Java 语言虚拟机后,Java 语言在不同平台上运行时不须要从新编译。Java 语言应用 Java 虚拟机屏蔽了与具体平台相干的信息,使得 Java 语言编译程序只需生成在 Java 虚拟机上运行的指标代码(字节码),就能够在多种平台上不加批改地运行。Java 虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是 Java 的可能“一次编译,到处运行”的起因。

Java 类加载过程

java 类加载须要经验一下 7 个过程:
1. 加载
加载是类加载的第一个过程, 在这个阶段, 将实现一下三件事件:

  • 通过一个类的全限定名获取该类的二进制流。
  • 将该二进制流中的动态存储构造转化为办法去运行时数据结构。
  • 在内存中生成该类的 class 对象, 作为该类的数据拜访入口。

2. 验证
验证的目标是为了确保 Class 文件的字节流中的信息不可危害到虚拟机。
在该阶段次要实现以下四种验证:

  • 文件格式验证: 验证字节流是否合乎 Clas 文件的标准, 如主次版本号是否在以后虚拟机范畴内, 常量池中的常量是否有不被反对的类型
  • 元数据验证: 对字节码形容的信息进行语义剖析, 如这个类是否有父类, 是否集成了不被继承的类等。
  • 字节码验证: 是整个验证过程中最简单的一个阶段, 通过验证数据流和控制流的剖析, 确定程序语义是否正确, 次要针对办法体的验证。如: 办法中的类型转换是否正确, 跳转指令是否正确等·符号援用验证: 这个动作在前面的解析过程中产生, 次要是为了确保解析动作能正确执行。

3. 筹备
筹备阶段是为类的动态变量分配内存并将其初始化为默认值, 这些内存都将在办法区中进行调配。筹备阶段不调配类中的实例变量的内存, 实例变量将会在对象实例化时随着对象一起调配在 Java 堆中。

// 在筹备阶段 vlue 初始值为 0, 在初始化阶段才会变为 123。public static int value=123;

4. 解析
该阶段次要实现符号援用到间接援用的转换动作。解析动作并不一定在初始化动作实现之前, 也有可能在初始化之后。

5. 初始化
初始化时类加载的最初一步, 后面的类加载过程, 除了在加载阶段用户应用程序能够通过自定义类加载器参加之外, 其余动作齐全由虚拟机主导和管制。到了初始化阶段, 才真正开始执行类中的定义的 java 程序代码。

6. 应用

7. 卸载

深拷贝和浅拷贝

  • 浅拷贝(shallowCopy):减少了一个指针指向已存在的内存地址。
  • 深拷贝(deepCopy):减少了一个指针并且申请了一个新的内存,使这个减少的指针指向这个新的内存。
  • 浅复制:指向被复制的内存地址,如果原地址产生扭转,那么浅复制进去的对象也会相应的扭转。
  • 深复制:在计算机中开拓一块新的内存地址用于寄存复制的对象。

应用深拷贝的状况下,开释内存的时候不会因为呈现浅拷贝时开释同一个内存的谬误。

常量池是什么

能够了解为 class 文件之中的资源仓库,它是 class 文件构造中与其余我的项目关联最多的数据类型,也是占用 class 文件空间最大的数据我的项目之一,同时它还是 class 文件中第一个呈现表类型的数据我的项目.

因为常量池的数量是不固定的,所以在常量池入口须要搁置一项 u2(即2个字节)类型的数据,代表常量池容量计数值(constant-pool-count)(从1开始,将0示意不援用任何常量).

常量池中次要寄存两大类常量:

  • 字面量(Literal):比拟靠近于 Java 语言层面的常量概念,如文本字符串,申明为 final 的常量值.
  • 符号援用(Synbolic Reference):包含如下三类常量:

①类和接口的全限定名(Fully Qualified Name)
②字段的名称和描述符(Descriptor)
③办法的名称和描述符

常量池在 class 文件的什么地位?

System.gc()和 Runtime.gc()会做什么事件?

用来提醒 JVM 要进行垃圾回收。然而, 立刻开始还是提早进行垃圾回收取决于 JVM。

Java 内存调配是怎么的?

  • 寄存器: 咱们无法控制。
  • 动态域: static 定义的动态成员。
  • 常量池: 编译时被确定并保留在.class 文件中的 (final) 常量值和和一些文本润饰的符号援用(类和接口的全限定名, 字段的名称和描述符, 办法和名称和描述符)。
  • 非 RAM 存储: 硬盘等永恒存储空间。
  • 堆内存:new 创立的对象和数组, 由 Java 虚拟机主动垃圾回收器治理, 存取速度慢。
  • 栈内存: 根本类型的变量和对象的援用变量(堆内存空间的拜访地址), 速度快, 能够共享, 然而大小与生存期必须确定, 不足灵活性。

新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?

新生代回收器:SerialParNewParallel Scavenge
老年代回收器:Serial OldParallel OldCMS
整堆回收器:G1
新生代垃圾回收器个别采纳的是复制算法,复制算法的长处是效率高,毛病是内存利用率低;老年代回收器个别采纳的是标记 - 整顿的算法进行垃圾回收。

简述分代垃圾回收器是怎么工作的?

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代应用的是复制算法,新生代里有 3 个分区:EdenTo SurvivorFrom Survivor,它们的默认占比是 8:1:1,它的执行流程如下:

  • Eden + From Survivor 存活的对象放入 To Survivor 区;
  • 清空 EdenFrom Survivor 分区;
  • From SurvivorTo Survivor 分区替换,From SurvivorTo SurvivorTo SurvivorFrom Survivor

每次在 From Survivor 到 To Survivor 挪动时都存活的对象,年龄就 +1,当年龄达到 15(默认配置是 15)时,降级为老生代。大对象也会间接进入老生代。
老生代当空间占用达到某个值之后就会触发全局垃圾发出,个别应用标记整顿的执行算法。以上这些周而复始就形成了整个分代垃圾回收的整体执行流程。

说一下 JVM 调优的工具?

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最罕用的是 jconsolejvisualvm 这两款视图监控工具。

  • jconsole:用于对 JVM 中的内存、线程和类等进行监控;
  • jvisualvm:JDK 自带的全能剖析工具,能够剖析:内存快照、线程快照、程序死锁、监控内存的变动、gc 变动等。

Java 堆的构造是什么样子的? 什么是堆中的永恒代(Perm Gen space)?

JVM 的堆是运行时数据区, 所有类的实例和数组都是在堆上分配内存。它在 JVM 启动的时候被创立。对象所占的堆内存是由主动内存管理系统也就是垃圾收集器回收。
堆内存是由存活和死亡的对象组成的。存活的对象是利用能够拜访的, 不会被垃圾回收。死亡的对象是利用不可拜访尚且还没有被垃圾收集器回收掉的对象。始终到垃圾收集器把这些对象回收掉之前, 他们会始终占据堆内存空间。

finalize()办法什么时候被调用? 析构函数 finalization)目标是什么?

垃圾回收器 (garbage collector) 决定回收某对象时, 就会运行该对象的 finalize 办法,然而在 Java 中很可怜, 如果内存总是短缺的, 那么垃圾回收可能永远不会进行, 也就是说 finalize 可能永远不被执行, 显然指望它做收尾工作是靠不住的。它最次要的用处是回收非凡渠道申请的内存。Java 程序有垃圾回收器, 所以个别状况下内存问题不必程序员操心。但有一种 JNI(Java Native Interface)调用 non-java 程序 (C 或 C ++), finalize() 的工作就是回收这部分的内存。

什么是分布式垃圾回收(DGC)? 它是如何工作的?

DGC 叫做分布式垃圾回收。RMI 应用 DGC 来做主动垃圾回收。因为 RMI 蕴含了跨虚拟机的近程对象的援用, 垃圾回收是很艰难的。DGC 应用援用计数算法来给近程对象提供主动内存治理。

正文完
 0