面试八股-JVM

news2024/11/28 9:48:13

1.Java代码编译过程

  1. 准备过程:初始化插入式注解处理器
  2. 解析与填充符号表过程
    1. 词法、语法分析,将字符流转为标记集合,构造抽象语法树
    2. 填充符号表,产生符号地址和符号信息
  3. 插入式注解处理器的注解处理
  4. 分析与字节码生成过程
    1. 标注检查,静态信息检查
    2. 数据流与控制流分析,动态检查
    3. 解语法糖
    4. 字节码生成

2.JVM组成和运行

2.1.组成

  • 类加载器:加载字节码,即class文件,只负责加载不管是否可以运行
  • 运行时数据区:存放数据的,Java内存方面的问题都集中在这里
  • 执行引擎:将class文件中的指令解释给操作系统,将JVM指令集翻译为操作系统指令集
  • 本地库接口:负责调用本地接口,调用不同语言的接口给Java用,如Java驱动,地图制作引擎等。这种方式主键被Socket通信、WebService等方式取代。

2.2.运行

  1. JVM的装入环境和配置,使用java.exe找JRE
    1. 自己目录下的JRE
    2. 父目录下的JRE
    3. 注册中注册的JRE
  2. 装载JVM,通过LoadJavaJVM装入JVM文件,LoadLibrary装载JVM动态连接库等。
  3. 初始化JVM,获得本地调用接口。
  4. 运行Java程序,jar包的话先从Manifest中获得Main-Class的值,然后main函数调用LoadClass装载该主类。Class的话直接LoadClass装载类。

3.类加载机制

3.1.类的生命周期与类加载阶段

类是在运行期间第一次使用时动态加载的,而不是一次性加载所有类。因为如果一次性加载,那么会占用很多的内存。

在这里插入图片描述

1.加载

加载过程完成以下三件事:

  • 通过类的完全限定名称获取定义该类的二进制字节流。
  • 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。
  • 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。

其中二进制字节流可以从以下方式中获取:

  • 从 ZIP 包读取,成为 JAR、EAR、WAR 格式的基础。
  • 从网络中获取,最典型的应用是 Applet。
  • 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理类的二进制字节流。
  • 由其他文件生成,例如由 JSP 文件生成对应的 Class 类。

2.验证

确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包括文件格式、元数据验证、字节码验证和符号引用验证。

3.准备

类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值(设置为0等),如果是final的话就直接设置为真实值,使用的是方法区的内存。

实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。

4.解析

将常量池的符号引用替换为直接引用的过程。

其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。

5.初始化

初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。

<clinit>() 是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。

接口中不可以使用静态语句块,但仍然有类变量初始化的赋值操作,因此接口与类一样都会生成 <clinit>() 方法。但接口与类不同的是,执行接口的 <clinit>() 方法不需要先执行父接口的 <clinit>() 方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的 <clinit>() 方法。

3.2.对象实例化过程

  • <init>()方法可以重载多个,类有几个构造器就有几个<init>方法
  • <init>()方法中的代码执行顺序为:父类变量初始化->父类代码块->父类构造器->子类变量初始化->子类代码块->子类构造器
  • 静态变量->静态代码块->普通变量->普通代码块->构造器

3.3.类初始化时机

1.主动引用

虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化

  • 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。
  • 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
  • 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  • 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类;
  • 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化

2.被动引用

以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。

  • 子类引用父类静态字段,子类不会初始化。
  • 通过数组引用类,不会引起初始化。
  • 常量引用时 ConstClass.HELLOWORLD

3.3.类与类加载器

两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。

这里的相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true。

3.4.类加载器分类

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

  • 启动类加载器(Bootstrap ClassLoader),使用 C++ 实现,是虚拟机自身的一部分;
  • 所有其它类的加载器,使用 Java 实现,独立于虚拟机,继承自抽象类 java.lang.ClassLoader。

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

  • 启动类加载器(Bootstrap ClassLoader)此类加载器负责将存放在 <JRE_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。
  • 扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。
  • 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

3.5.双亲委派模型

类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L15GQkYW-1670858358621)(/home/zekdot/桌面/note/java/figure/parent.png)]

1. 工作过程

一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。

2.好处

  • 避免类重复加载:父加载器加载某个类后子加载器不用重复加载
  • 避免篡改核心类:如自定义的String类不会加载,只会加载java自带String类

3. 实现

以下是抽象类 java.lang.ClassLoader 的代码片段,其中的 loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。

4.破坏

并不是具有强制约束的模型,而是推荐的类加载器实现方式。Java模块化出现为止,出现过3次较大规模“被破坏”情况。

  • 双亲委派模式出现之前的实现。
  • 模型自身缺陷导致的,如JNDI服务,逆向使用类加载器。
  • 追求程序动态性,热代码替换等,OSGi。

5.SPI和打破双亲委派机制

打破:

  • 集成classLoader自定义类加载器,重写loadClass方法
  • 使用线程上下文加载器(SPI实现)

SPI

  • Java提供的一套用来被第三方实现或者扩展的接口
  • 遵守约定,类名写到/META-INF里,启动后扫描jar包里符合约定的类名,然后forName加载

4.运行时数据区域

4.1.线程内部

  • 程序计数器:记录正在执行的虚拟机字节码指令的地址(执行本地方法则为空)。
  • Java虚拟机栈:每个Java方法执行同时会创建栈帧用于存储局部变量表、操作数栈、常量池引用等信息。可以通过-Xss指定Java虚拟机栈内存大小,1.4默认256K,1.5以上默认1M。可能存在StackOverflowError异常和OutOfMemoryError异常。
  • 本地方法栈:本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。

4.2.堆

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

  • 新生代 1
    • Eden 区8
    • Survivor 区1,为了解决太多对象放入老年区的问题,解决内存碎片
  • 老年代 2

堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。

可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。

4.3.方法区

用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。

对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。

从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。

方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中。

运行时常量池

运行时常量池是方法区的一部分。

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

除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

4.4.直接内存

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

5.垃圾收集

针对堆和方法区进行的,线程生命周期内的在其结束后就会释放,所以不需要回收。

5.1.判断一个对象是否可以回收

1.引用计数算法

对象增加一个引用计数器加一,失效时减一,但是可能存在循环引用。

2.可达性算法

以GC Roots为起始点进行搜索,可达存活,不可达回收。

RC Root包含以下内容。

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

3.方法区回收

方法区存放永久代,回收性价比不高,主要是常量池回收和类卸载(反射和动态代理中)。

  • 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。

4.finalize

类似析构函数,用于关闭外部资源,但是不如try-finally方式,代价高,不确定性大。可以实现方法的自救。

5.2.引用类型

判定对象是否可以回收。

1.强引用

被强引用关联的对象不会被回收,使用new方式创建的就是强引用。

2.软引用

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联

只有内存不够的时候才会回收,使用SoftReference来创建。

3.弱引用

被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。使用 WeakReference 类来创建弱引用。

4.虚引用

又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。使用 PhantomReference 来创建虚引用。

5.3.垃圾收集算法

1.标记-清除

在分配时,程序会搜索空闲链表寻找空间大于等于新对象大小 size 的块 block。如果它找到的块等于 size,会直接返回这个分块;如果找到的块大于 size,会将块分割成大小为 size 与 (block - size) 的两部分,返回大小为 size 的分块,并把大小为 (block - size) 的块返回给空闲链表。

  • 标记和清除过程效率都不高;
  • 会产生大量不连续的内存碎片,导致无法给大对象分配内存。

2.标记-整理

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。老年代用法。

优点:

  • 不会产生内存碎片

缺点:

  • 需要移动大量对象,处理效率比较低。

3.复制

内存划分相等两块,每次用其中一块,用完之后将存活对象复制到另一块,然后清理。

主要不足是只使用了内存的一半。现在的商业虚拟机采用这种算法回收新生代,但是不是相等两块,而是一块较大Eden空间和两块较小的Survivor空间。每次使用Eden和其中一个Survivor,回收时将Eden和Survivor存活对象都放到另一块Survivor中。默认比例为8:1,内存利用率达到90%,如果Survivor不够用,需要借用老年代空间。

4.分代收集

现代商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。

一般将堆分为新生代和老年代。

  • 新生代使用:复制算法
  • 老年代使用:标记 - 清除 或者 标记 - 整理 算法

5.4.垃圾收集器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNmJKzGV-1670858358622)(/home/zekdot/桌面/note/java/figure/GC1.jpg)]

连线可以配合使用。

  • 单线程与多线程:单线程指的是垃圾收集器只使用一个线程,而多线程使用多个线程;
  • 串行与并行:串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候需要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 之外,其它垃圾收集器都是以串行的方式执行

1.Serial收集器

  • 串行的方式执行。是单线程的收集器,只会使用一个线程进行垃圾收集工作。

  • 是 Client 场景下的默认新生代收集器

2.ParNew收集器

  • Serial 收集器的多线程版本。
  • Server场景下的默认新生代收集器。

3.Parallel Scavenge收集器

  • 多线程收集器。
  • 吞吐量优先。这里的吞吐量指 CPU 用于运行用户程序的时间占总时间的比值。

4.Serial Old 收集器

是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途:

  • 在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用。
  • 作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。

5.Parallel Old收集器

Parallel Scavenge收集器的老年代版本。在注重吞吐量以及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。

6.CMS收集器

Concurrent Mark Sweep

  • 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。
  • 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。
  • 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。
  • 并发清除:不需要停顿。

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

7.G1收集器

一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9ig3yOs-1670858358623)(/home/zekdot/桌面/note/java/figure/G1.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qDdxYBEq-1670858358623)(/home/zekdot/桌面/note/java/figure/G12.png)]

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

  • 初始标记:暂停用户线程,标记GCRoot直接关联的对象
  • 并发标记:从GCRoot进行可达性分析,并标记存活对象,与用户线程并发。
  • 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。
  • 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。

具备如下特点:

  • 空间整合:整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。
  • 可预测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。

6.内存分配与回收策略

6.1.Minor GC触发条件

Eden区空间耗尽时,存活下来的对象直接送到Survivor区。

6.2.Minor GC 和 Full GC

  • Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。
  • Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。

6.3.内存分配策略

1.对象优先在Eden分配

大多数情况下在Eden上分配,空间不够时,发起Minor GC。

2.大对象直接分配在老年代

需要连续存储空间,比如大字符串或者数组。-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。

3.长期存活的对象进入老年代

为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,在Survior区中每熬过一次Minor GC,年龄就增加 1 岁,增加到一定年龄(默认15)则移动到老年代中。

-XX:MaxTenuringThreshold 用来定义年龄的阈值。

4.动态对象年龄绑定

如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

5.空间分配担保

在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。

6.4.Full GC 的触发条件

对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件

1.调用System.gc

只是建议虚拟机执行,但是不一定真的执行。

2.老年代空间不足

大对象直接进入老年代、长期存活的对象进入老年代等。

应当尽量不要创建过大的对象以及数组

通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代

通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间

3.空间分配担保失败

担保失败时进行Full GC。

4.JDK 1.7之前永久代空间不足

HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。

当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。

为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。

5.Concurrent Mode Failure

执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。

6.5.内存泄露与内存溢出

1.内存泄露

程序运行过程中分配内存给临时变量,用完之后没有被GC回收,始终占用内存,不能使用也不能分配给其他程序。

分类:

  • 常发性:每次执行都要导致一块内存泄露
  • 偶发性:某些特定环境或者操作才会发生
  • 一次性:发生内存泄露的代码只会执行一次,且一块内存发生泄露
  • 隐式:没有及时释放导致内存不可用

建议

  • 尽早释放无用对象引用
  • 避免循环中创建对象
  • 使用字符串处理避免使用String,使用StringBuffer
  • 少用静态变量

2.内存溢出

申请内存大于系统可以提供的内存。

原因

  • 内存中加载的数据量过大
  • 集合类中引用对象,使用完后未清空
  • 代码中存在死循环或循环产生过多重复对象实体
  • 第三方软件的BUG
  • 启动参数内存值设定过小

方案

  • 修改JVM启动参数,直接增加内存
  • 检查错误日志,查看是否有其他错误或者异常
  • 对代码走查分析,找出可能发生内存溢出的位置
  • 使用内存查看工具动态查看内存使用情况

除了程序计数器外,虚拟机内存的其他几个运行区域都有可能OOM异常。

7.JVM调优

7.1.需要关注的指标

  • 内存使用情况
  • 延迟:垃圾回收造成的延迟
  • 吞吐量:程序运行时间占包括运行时间和垃圾回收时间总时间之比

7.2.调优正常运行的系统

  1. 通过jmap查看各个区域的使用情况
  2. 通过jstack查看线程的运行情况,是否有阻塞,是否出现死锁
  3. 通过jstat查看垃圾回收情况,比如fullgc次数过多,有可能是年轻代太小,大对象直接放入老年代,可以增加年轻代大小
  4. 通过监控工具(基于命令等)
  5. 发现异常后进行定位优化,比如找到占用CPU 多的线程,对方法进行优化

7.3.对于已经发生OOM的系统

  1. 通过JVM配置,让系统发生OOM后生成dump文件
  2. 使用工具分析dump文件
  3. 根据dump文件找到异常的实例对象和异常线程,比如某个实例占用太多内存
  4. 进行调优

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85566.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

描述统计 | 学习笔记

一.导论 统计学是通过收集&#xff0c;整理&#xff0c;分析&#xff0c;描述数据等手段&#xff0c;以达到推断所测对象的本质&#xff0c;甚至预测对象未来的一门综合性科学。其目的是探索数据的内在数量规律性&#xff0c;以达到对客观事物的科学认识 统计的本业是消化数据…

java语言【#107. 七的奇倍数】(已通过)

题目描述 ​ 如果一个数既是 7 的倍数又不能被 2 整除&#xff0c;那么我们称之为七的奇倍数。 ​ 输入一个正整数 n&#xff0c;判断它是否是 7 的奇倍数。 输入 ​ 输入一个正整数 n &#xff08;0≤n≤100&#xff09; 输出 ​ 如果 n 是 7 的奇倍数 输出 YES 否则输出 NO…

校园二手市场开题报告范文

目录 一、课题意义&#xff08;包括课题的理论意义和现实意义&#xff09; &#xff08;一&#xff09;理论意义 &#xff08;二&#xff09;现实意义 二、文献综述&#xff08;包括&#xff1a;1.理论的渊源及演进过程2.国内外对本课题的研究现状和有待解决的问题3.本人对所…

适合rv1109+rv1126使用基于RKmedia的人脸和车牌识别的SDK及使用介绍说明

基于RKmedia开发的人脸识别和车牌识别SDK简介及使用 一、功能简介 SDK下载&#xff1a;https://download.csdn.net/download/DeepLearning_/87272848 人脸检测&识别 对于人脸部分&#xff0c;SDK 提供了以下能力&#xff1a; 1. 人脸检测 1.1 人脸是否带口罩判定 1.2…

路由器的安装方法步骤

路由器的安装和设置如下&#xff1a; 1.首先连接线路&#xff0c;可以参考下图 2.线路连接完毕后&#xff0c;打开浏览器输入路由器的管理地址和管理密码然后进入路由器后台&#xff08;具体路由器的登录地址和登录密码可以参考说明书或者是路由器背面的标签&#xff09; 3.…

Nacos配置管理-配置热更新

Nacos配置自动刷新 Nacos中的配置文件变更后&#xff0c;微服务无需重启就可以感知。不过需要通过下面两种配置实现&#xff1a; 热更新-方法一&#xff1a;使用RefreshScope注解 在Value注入的变量所在类上添加RefreshScope注解&#xff1b; RestController RequestMapping(&…

直播|BIA Separations 和元生物两位大咖关于质粒DNA的制造工艺和质量控制

质粒 DNA&#xff08;pDNA&#xff09; 是生物研究和治疗开发中用于转移或表达基因的一个基本组成部分。细胞与基因治疗中最常用的载体AAV和慢病毒的生产都需要质粒作为起始材料&#xff0c;随着基因治疗&#xff0c;细胞治疗及DNA疫苗的快速发展&#xff0c;快速高效的大规模工…

腾讯云原生数据湖存储服务能力再上新,三级加速体系助力企业用数赋智

随着数据价值被越来越多企业认可&#xff0c;数据湖存储已成为企业级存储的首选和新一代提升生产力的服务。12月1日&#xff0c;在2022腾讯数字生态大会存储专场&#xff0c;腾讯云升级了云原生数据湖产品能力&#xff0c;并详细阐释了其设计理念&#xff0c;对其在多个行业的应…

数据万象技术演进之路

前言 Dale&#xff08;1969&#xff09;的“经验之塔”指出人们获取信息70%以上是通过视觉途径&#xff0c;在互联网时代也不例外。在当今图片音视频服务等已成为一个互联网应用中占比很大的部分&#xff0c;对图片和音视频等数据的处理能力也相应变成企业和开发者的一项基本技…

C++中二叉树的非递归遍历方法2-2

4 代码实现二叉树的非递归遍历 在“2 二叉树的遍历方法”中提到&#xff0c;二叉树的遍历方法有前序遍历、中序遍历、后序遍历属于深度优先遍历。接下来以前序遍历为例&#xff0c;通过代码实现该方法的二叉树非递归遍历。 4.1 前序遍历 4.1.1 前序遍历的非递归步骤 因为前…

记一次go协程读写锁 sync.RWMutex未释放导致其他协程阻塞bug

记一次go协程读写锁 sync.RWMutex未释放导致其他协程阻塞bug记一次go协程读写锁 sync.RWMutex未释放导致其他协程阻塞bug用到的监测工具程序简要介绍示例代码运行结果运行结果分析记一次go协程读写锁 sync.RWMutex未释放导致其他协程阻塞bug 通过一个简单示例模拟某协程结束&a…

[附源码]Python计算机毕业设计电影院订票系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

第53篇 Qt Quick项目详解

导语 前面我们一起创建了一个Qt Quick项目&#xff0c;并对里面的文件进行了简单的讲解&#xff0c;虽然这只是一个HelloWorld程序&#xff0c;但对于没有Qt Quick编程经验的同学来说&#xff0c;这个项目还是有点复杂。在这一篇中&#xff0c;我们将从最简单的QML文件讲起&am…

Flutter Web CORS解决方案1-禁用浏览器安全策略

Flutter Web CORS解决方案1设置CHROME_EXECUTABLE关于 CHROME_EXECUTABLE创建 CHROME_EXECUTABLE修改 chrome.dart 禁用安全策略升级 flutterSDK 后需重新修改升级后指定--web-hostname参数问题浏览器启用 Allow-CORS 插件部分协议OPTIONS预检跨域问题本文介绍第一种解决Flutte…

《纳瓦尔宝典》笔记一——你是在跟自己竞争,这是一场单人游戏

目录 一、身体的健康是top1 二、你唯一拥有的就是时间 三、选择短期内更更痛苦的道路 四、人生早期有三个重大决定 五、从雇佣关系中解脱出来 六、找合作伙伴 七、你真的嫉妒别人吗 八、从期待中解放出来 九、向内求-内在的评价标准 十、人生的大赢家就是同时拥有时间…

[附源码]Nodejs计算机毕业设计基于RationalRose的教务管理系统开发Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

基于java+swing+mysql图书管理系统3

大作业-基于java swing图书管理系统3一、系统介绍二、功能展示1.管理员登陆2.图书类别添加3.图书类别维护4.图书添加5.图书维护三、系统实现1.BookManageMainFrame.java四、其它1.其他系统实现五.获取源码一、系统介绍 该系统实现了用户登陆、图书类别管理(图书类别添加、图书…

JavaEE 初始化两个上下文对象,导致更新网站访问次数更新失败

问题描述 &#xff1a; 在做网站次数统计的时候&#xff0c;需要用到上下文对象&#xff0c;最终在上下文监听中发现上下文对象创建两次&#xff0c;销毁两次&#xff0c;导致数据库中网站访问次数统计更新失败。 原因 &#xff1a; 我们更改了项目的访问路径&#xff0c;就会导…

使用Java实现上传图片到七牛云

文章目录1.登录七牛云官网&#xff0c;注册账号并登录2.在项目中导入七牛云依赖3.编写创建文件名工具类4.编写连接七牛云工具类5.编写前端请求的Controller6.上传成功1.登录七牛云官网&#xff0c;注册账号并登录 2.在项目中导入七牛云依赖 <!-- 七牛云依赖 --> <!-…

力扣1832.判断句子是否为全字母句(cpp实现+解析)

文章目录1832.判断句子是否为全字母句解法一解法二解法三&#xff08;最优&#xff09;1832.判断句子是否为全字母句 难度&#xff1a;简单 力扣传送门&#xff1a; https://leetcode.cn/problems/check-if-the-sentence-is-pangram/description/ 题目要求&#xff1a; 全字…