Checklist系列:JVM自检四十问,万字整理,推荐收藏

基础

请简单的介绍一下jvm?

  • JVM

    • 全称:Java Virtual Machine(Java虚拟机)
    • 简介:JVM是一种虚拟机,它使计算机能够运行Java程序以及用其他语言编写并编译为Java字节码的程序。Java的设计理念之一是"一次编写,到处运行",JVM的存在使得Java程序具有跨平台性,不受底层硬件和操作系统的影响。
    • JVM规范
      • 类加载器:负责加载、链接和初始化Java类。
      • 虚拟机架构:定义了特定数据类型、高效的垃圾收集器以及内存结构。
      • 字节码指令: JVM有自己的指令集,使得相同的字节码在不同操作系统上都有相同的语义。
      • JVM语言:允许其他编程语言通过生成Java字节码来运行在JVM上,例如Groovy、Scala等。
      • 字节码验证器:确保字节码是安全的,防止恶意代码的执行。
      • 字节码解释器和即时编译器:
        • 字节码解释器:将字节码解释为机器码,针对不同的硬件架构适配,使得Java程序可以在不同的硬件和操作系统动态地适应环境。
        • 即时编译器:将字节码转换为本地机器码,提高程序的执行效率。
  • 参考链接:https://en.wikipedia.org/wiki/Java_virtual_machine

Java有哪些内存区域?

  • Java内存区域:一般是指程序运行期间,使用的各种运行时期使用的各种运行时数据区域。
    • PC寄存器:线程私有。PC寄存器存储当前正在执行的 Java 虚拟机指令的地址,确保线程在正确的位置执行指令。
    • 栈:线程私有,用于存储栈帧。栈帧包含了局部变量表、操作数栈、动态链接、方法出口等信息。栈中存储方法调用和局部变量等信息。
    • 堆:线程共享区域。所有类的实例(对象),数组都是在这里分配。由垃圾收集器负责释放对象。
    • 方法区:线程共享区域。它存储每个类的结构,时常量池。逻辑上属于堆结构
    • 运行时常量池:运行时常量池是方法区的一部分,用于存储class文件中的常量池信息。包括类、接口中的常量,字符串常量等。
    • Native方法栈:Native方法栈是用于执行native方法,和栈类似。这个区域不一定在所有的Java虚拟机实现中都存在

大致结构如下所示,根据官网描述,拆分成了共享区域和线程私有区域。

  • 官网链接:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

栈帧都有哪些数据?

  • 局部变量表(Local Variable Table): 用于存储方法中定义的局部变量。
  • 操作数栈(Operand Stack): 用于执行方法时进行计算的栈结构。操作数栈存储了方法执行过程中的操作数,以及执行过程中的中间结果。
  • 动态链接(Dynamic Linking): 用于在运行时解析常量池中的符号引用。
  • 方法返回地址(Return Address): 用于存储方法调用结束后返回的地址。

一下JDK1.6、1.7、1.8内存区域的变化?

  • 1.6 标准的JVM方法区设计规范
  • 1.7 将字符串常量池移入堆
  • 1.8 去掉永久代,取而代之的是元空间,并且不在堆中,而是放入本地内存

  • 官网链接:
  • https://www.oracle.com/java/technologies/javase/jdk7-relnotes.html
  • https://openjdk.org/jeps/122

为什么使用元空间替代永久代作为方法区的实现?

主要原因有以下三点

  • JRockit没有永久代,希望可以JRockit和Hotspot融合。

  • 永久代启动时固定大小,难以调整。受到-XX:MaxPermSize参数的控制,在实际使用的过程中有些不便,容易造成OOM

  • 字符串常量会收到类回收的影响,类被卸载,与之关联的字符串常量也会被回收。

  • 官网链接:https://openjdk.org/jeps/122

类的加载过程是?

  • 加载(Loading):编译过后的字节码文件加载进JVM,存储在方法区中。
  • 链接(Linking):获取类并将其组合到Java虚拟机的运行时状态以便可以执行的过程。
    • 验证(Verification):验证二进制结构正确,此过程可能会导致加载其他类和接口
    • 准备(Preparation):为类或接口创建静态字段并将这些字段初始化为其默认值。
    • 解析(Resolution):解析是从运行时常量池的符号引用中动态确定具体值的过程。
  • 初始化(Initialization):执行类初始化方法,如上面静态变量的常量值,静态代码块之类的,在这一步执行。以下情况会初始化
    • 通过JVM指令引发的初始化:new、getstatic、putstatic或invokestatic
    • 首次调用MethodHandle实例
    • 调用class库中的反射方法
    • 子类被初始化
    • 指定为Java虚拟机启动时的初始类。

  • 官网链接:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html

Java的双亲委派机制是什么?

双亲委派(Parents Delegation Model):
当加载一个类的时候,如果类不存在,会先交给父类进行加载(前提是没有破坏双亲委派模型),如果父加载器无法完成,自己才会去加载这个类加载器。

/**
* 1. 调用 findLoadedClass(String) 以检查该类是否已加载。
* 2. 在父类加载器上调用 loadClass 方法。如果父级为空,则使用虚拟机内置的类加载器。
* 3. 调用 findClass(String) 方法来查找该类。
*/
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查类是被加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //调用父类loadClass
                        c = parent.loadClass(name, false);
                    } else {
                      //使用虚拟机内置的类加载器(BootStrap)。
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    //使用findClass来找到类
                    long t1 = System.nanoTime();
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

以上就是简略版本的双亲委派模型的答案整理。下面是一些多余的衍生小问题,也许用的上呢

  1. 双亲委派模型的构成
    我能找到的入口,就是Launcher类,其中AppClassLoader指定了ExtClassLoader为父类,ExtClassLoader并没有指定父类,而是通过上面的loadClass如果parent为null则交给BootstrapClassLoader处理,BootstrapClassLoader也是JVM中创建并管理。
public Launcher() {
        ExtClassLoader var1;
        
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
      
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
       

        Thread.currentThread().setContextClassLoader(this.loader);

    }
  1. BootstrapClassLoaderExtClassLoaderAppClassLoader各自管理的部分
  • BootstrapClassLoader:System.getProperty("sun.boot.class.path")

  • ExtClassLoader:System.getProperty("java.ext.dirs")
    对应如下包

  • AppClassLoader:System.getProperty("java.class.path")

  1. 双亲委派核心方法
  • loadClass:加载类,双亲委派实现的关键
  • findClass:从URL查找并加载指定的类,具体加载实现为defineClass

为什么需要双亲委派机制?

双亲委派模型保证了,无论什么类加载器,获取一个类,都先由父加载器先加载,因此,访问这个类,返回的也都是同一个。

如何破坏双亲委派机制?

如果想打破,直接重写loadClass即可

如果不想打破,重写findClass

对象创建的过程是?

  1. 检查类是否被加载:会去运行时常量池中查找该引用所指向的类有没有被JVM加载,如果没有被加载,那么会走上面类加载的过程
  2. 空间分配:为对象在栈中和堆中分配一定的空间。
  3. 初始化:JVM会将对象的字段设置为默认值。
  4. 信息标记:虚拟机对对象进行必要的设置,包括该对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些关键信息存放在对象的对象头中。
  5. 执行构造方法:执行对象的构造方法。

  • 参考链接:https://www.cnblogs.com/xfeiyun/p/15600513.html

能说一下类的生命周期吗?

和类加载机制相比多了使用了卸载

分配内存对象中的指针碰撞和空闲列表说下?

  • 指针碰撞(Bump the pointer)
  • Java堆中的内存是规整的(标记整理),所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离。例如:Serial、ParNew等收集器没有产生内存碎片,所以采用的是"指针碰撞"。
  • 空闲列表(Free List)
  • Java堆中的内存不是规整的(标记清除),已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。例如:CMS这种基于"标记-清除"的收集器,会产生内存碎片,空闲内存跟已经分配的内存相互交错,所以采用的是"空闲列表"。
  • 参考链接:https://www.cnblogs.com/xfeiyun/p/15600513.html

内存溢出和内存泄漏是什么意思

  • 内存溢出:我想要申请空间,但是空间不够了,地主家实在是没有余粮了,GC后也不够,伴随着这个报错OutOfMemoryError
  • 内存泄漏:这个对象没有用了,但是还是存活,拜拜浪费内存空间,如果放任不管最终会导致内存溢出

强引用、软引用、弱引用、虚引用是什么?

这个我之前写过博客,感兴趣可以看下链接

这里还是总结一下

  • 强引用:平时创建对象的方式就是强引用。GC绝对不会回收,内存不够报OOM,也不会回收
Object obj =new Object();
  • 软引用:只有在OOM发生前才会对他进行回收。
list[i]=new SoftReference<Element>(new Element(i));
  • 弱引用:只存在弱引用的对象会在下一次GC时被回收
list[i]=new WeakReference<Element>(new Element(i));
  • 虚引用:用于代替finalize,在对象被gc的时候执行clear方法

你平时工作中用过的JVM常用基本配置参数有哪些?

堆配置:

  • -Xms:设置JVM初始内存。
  • -Xmx:设置最大堆大小。
  • -Xmn:设置年轻代大小。
  • -Xss:设置线程的栈大小。
  • -XX:NewRatio:设置年轻代和年老代的比值。
  • -XX:SurvivorRatio:年轻代中Eden区与两个Survivor区的比值。
  • -XX:MaxPermSize:设置永久代大小(1.7)
  • -XX:MaxMetaspaceSize:元空间最大配置
  • -XX:MaxTenuringThreshold:晋升到老年代的年龄

gc配置

  • -XX:+UseParallelGC:选择垃圾收集器为并行收集器。
  • -XX:ParallelGCThreads:配置并行收集器的线程数,即同时多少个线程一起进行垃圾回收。
  • -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。
  • -XX:MaxGCPauseMillis:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
  • -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器自动选择年轻代区大小和相应的Survivor区比例
  • -XX:+UseConcMarkSweepGC:设置年老代为CMS。
  • -XX:+UseParNewGC:设置年轻代为parNew
  • -XX:CMSFullGCsBeforeCompaction:设置运行多少次GC以后对内存空间进行压缩、整理。
  • -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。
  • -XX:ParallelCMSThreads:CMS使用的线程数
  • -XX:+CMSScavengeBeforeRemark:重标记前做一次younggc
  • -XX:+UseG1GC:设置垃圾收集器为G1

其他:

  • -XX:+PrintGC:用于输出GC日志。

  • -XX:+HeapDumpOnOutOfMemoryError:内存溢出时导出整个堆信息

  • -XX:HeapDumpPath:导出堆的存放路径

  • 参考链接:https://www.ibm.com/docs/en/sdk-java-technology/8?topic=options-xxparallelcmsthreads

请你谈谈对OOM的认识

OOM:指的是java.lang.OutOfMemoryError,它一出现就代表,再堆中没有空间分配对象了,FullGC后也没有空间了

官网中列出了如下几种情况我见过的可能就第一种=。=

  • Java heap space:最常见的情况。他的出现就是FullGC后的堆内存不够分配给对象了,它的出现,要么对象过大,要么内存泄漏引起的

  • GC Overhead limit exceeded:花费过多的时间在GC上并且释放了很少的内存。可以被-XX:-UseGCOverheadLimit关闭

  • Requested array size exceeds VM limit:数组大小超过了VM的限制

  • Metaspace:元空间耗尽了,一般呢和-XX:MaxMetaspaceSize优点关系

  • request size bytes for reason. Out of swap space?(Native Heap):JVM尝试在本地堆分配内存,但是本地堆已经快耗尽了。

    • 看的我比较晕,我的理解是:内存+swap>堆空间>内存,可能其他程序导致了swap空间不够引发的。
  • Compressed class space:跟类指针压缩有关UseCompressedClassPointers,如果超过了另一个配置CompressedClassSpaceSize会报错

  • reason stack_trace_with_native_method:JNI中发生了OOM

  • 官网链接:https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html

如何排查内存溢出?

亲身经历了多次OOM,可以很负责任的告诉你们,就是按照如下几个步骤来

  1. 发现OOM

1.1. 已经发生了

一般都是有告警,这个挽救已经来不及了,跳到下面看看怎么分析并处理吧
还记得这两个配置吗,这时候已经有文件了。

  • -XX:+HeapDumpOnOutOfMemoryError:内存溢出时导出整个堆信息
  • -XX:HeapDumpPath:导出堆的存放路径

1.2. 还没发生,快要发生了
这个一般伴随着几个特征

  • 频繁Full GC
  • Full GC后内存下不去
    针对频繁Full GC,可以查看gc log,针对FullGC后内存下不去,这个方法就比较多了,我比较常用的是jstat,主要查看FullGC后的堆的大小。

不管符不符合,都可以dump内存然后重启了

  1. 分析OOM

虽然官网列了这么多种,但是,基本也就需要处理第一种情况(因为我处理过这么多次都是第一种=。=),就是堆内存不够的情况。

JProfile中就可以进行分析,看看Biggest Object就行。然后查看调用栈。还需要稍微关注下,List中的数量,也就是内存泄漏的重灾区。

OOM会造成程序退出吗

这个要分成多个问题来看

  1. JVM退出的场景
    jvm退出场景是由于以下场景之一
  • 正常退出场景:
    • 当应用程序的所有非守护线程执行完成(没有非守护线程)
    • 调用java.lang.System.exit方法
  • 异常退出场景:
    • JVM被迫中断(ctrl+C 或者 SIGINT指令)
    • JVM被终止(SIGTERM指令)
  1. 线程退出的场景

通过上面看到JVM退出的场景之一就是所有的非守护线程都不存在,所以下面要列一下非守护线程不存在的几个场景

  • 正常场景
    • run方法执行完成
    • 调用java.lang.System.exit方法
    • 它是守护线程,所有非守护线程死亡
  • 非正常场景
    • Exception或者Error抛出或未处理

当线程申请内存的时候会抛出OOM,但它本身并不会导致JVM退出。
所以,OOM和造成JVM退出之间并没有什么关联关系。在单个线程中发生OOM错误通常只会影响到该线程。其他线程仍然可以正常执行,因此,OOM错误与JVM退出之间的直接没有什么关系。

下面是验证子线程,出现OOM的场景

只会导致当前线程退出,对于其他线程不受影响。

public static void main(String[] args) {
        new Thread(()->{
            List<Object> list=new ArrayList<>();
            while (true){
                list.add(new Object());
            }
        }).start();
        new Thread(()->{
            while (true){
                System.out.println(1111);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

  • 参考链接:
  • https://stackoverflow.com/questions/32315589/what-happens-when-the-jvm-is-terminated
  • https://stackoverflow.com/questions/1269931/when-does-a-java-thread-reach-the-die-state

Java堆为什么要分代回收?

Java堆组成结构如下,划分为年轻代和老年代,年轻代又分成Eden区和Survivor区。这么分配的目的,是为了针对不同存活时间的对象采用更加高效的算法。年轻代主要特点就是朝生夕灭,这样用标记复制算法就特别高效,老年代存活时间长(经历年轻代层层筛选),需要的垃圾清理频次相对而言要少的多,这样标记整理或者标记清除更合适。

  • 年轻代
    • Eden区
    • Survivor区
      • From Survivor区
      • To Survivor区
  • 老年代

-XX:Newratio配置年轻代和老年代-XX:SurvivorRatio配置eden区和survivor的区域

JIT是什么?

JIT(Just-in-time compilation):在程序运行时编译代码。

  • 传统的解释器在首次运行前将所有字节码解释成机器语言。JIT则是在运行时也可以编译
  • JIT编译器可以访问动态运行时信息,而传统的解释器器不能。因此,JIT编译器可以进行更好的优化,将热点代码编译成机器码,从而进行优化。

  • 参考链接:https://stackoverflow.com/questions/95635/what-does-a-just-in-time-jit-compiler-do/95679#95679

有没有了解逃逸分析技术?对象一定分配在堆中吗?

  • 逃逸分析:简单的说,排查一下这个对象在哪被引用到了。对于逃逸的程度定义了三类。
typedef enum {
   NoEscape = 1,   
   ArgEscape = 2,             
   GlobalEscape = 3 
}
  • NoEscape: 没有发生逃逸,可以被替换成标量,可以不分配在堆中
  • ArgEscape:被当做参数从一个方法传递给另一个方法,但是没有发生逃逸
  • GlobalEscape:发生了逃逸

对于第一种NoEscape,对象可以替代成一个变量,那么分配在堆中也没有必要了,直接在栈中分配,无需GC管理。官网给了这个例子,如果全部在堆中,则要分配1亿个对象,如果标量替换,一旦对象返回,则立马释放。

public class Rect { 
   private int w; 
   private int h;
   public Rect(int w, int h) {
      this.w = w; 
      this.h = h; 
   }
   public int area() { 
      return w * h; 
   }
   public boolean sameArea(Rect other) {
      return this.area() == other.area(); 
   }
   public static void main(final String[] args) { 
      java.util.Random rand = new java.util.Random();
      int sameArea = 0;
      for (int i = 0; i < 100_000_000; i++) {
         Rect r1 = new Rect(rand.nextInt(5), rand.nextInt(5)); 
         Rect r2 = new Rect(rand.nextInt(5), rand.nextInt(5));
         if (r1.sameArea(r2)) { sameArea++; }
      }
      System.out.println("Same area: " + sameArea);
   }
}
  • 官网链接:https://blogs.oracle.com/javamagazine/post/escape-analysis-in-the-hotspot-jit-compiler

JIT中除了缓存热点代码外还有没有其他优化?

  • 逃逸分析:对象的引用进行分析
  • 锁消除:如果对象没有被其他线程访问,则synchronized会被消除
  • 标量替换:对象替换成变量
  • 栈中分配:在中分配对象

GC

Java中可作为GC Roots的对象有哪几种?

以下是Java中可作为GC Roots的对象的几种主要类型。GC Roots是一组引用,通过这组引用,可以追踪到所有存活的对象,确保不会对活跃对象进行垃圾回收。

  • Class:由系统类加载器加载的类;还包含对静态变量的引用。

  • 本地栈:本地栈中存储的变量和方法参数。如果一个对象被本地栈引用,说明它是一个活跃对象,不能被回收。

  • 活跃的Java线程:所有的活跃的Java线程

  • JNI References:JNI调用的创建的Java对象,包含本地变量,JNI方法参数和全局JNI引用

  • 锁对象:重量级锁。因为锁对象通常会被多个线程共享,需要确保在使用锁的时候锁对象不会被垃圾回收。

  • 由JVM实现定义的特定对象: 这包括一些由Java虚拟机实现定义的特殊对象,例如重要的异常类、系统类加载器或自定义类加载器。这些对象可能被视为GC Roots,以确保系统的正常运行。

  • 参考链接:https://www.baeldung.com/java-gc-roots#types-of-gc-roots

JVM垃圾回收时候如何确定垃圾?

主要是两种方法,引用计数法和可达性分析算法

  1. 引用计数法:
  • 原理:每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
  • 缺点: 无法解决循环引用的问题。如果存在循环引用,引用计数法会导致计数器值永远不为零,即使这些对象实际上已经不再被程序所使用。
  1. 可达性分析算法:
  • 原理:通过上面的GC Roots为根节点,向下搜索,可达的(可以搜索到的)都是活跃的,不能被回收,而无法到达的对象即为垃圾。
  • 缺点:有STW时间,要避免新对象对引用链的影响,所以,会有一定的STW时间,这也是最常见的问题。

GC垃圾回收算法了解吗?与垃圾收集器的关系?

  • 标记清除算法:标记和清除两大部分,先标记存活对象,再清除未被标记的对象

    • 优点:简单,快(STW时间少)
    • 缺点:
      • 容易产生碎片
      • 为对象分配空间速度慢
      • 效率不稳定,需要回收的垃圾越多就越慢
    • 垃圾收集器:CMS
  • 标记复制算法:标记同上,不过不再是直接清除,而是复制到另一块上面,再将原来的清除。

    • 优点:
      • 如果存活对象少,这个速度会很快
    • 缺点:
      • 浪费的内存多
    • 垃圾收集器:Serial、ParNew、Parallel Scavenge

  • 标记整理算法:和上面类似标记和整理两大部分,先标记存活对象,然后让所有存活的对象都向内存空间一端移动,最后直接清理掉边界以外的内存
    • 优点:
      • 解决了标记清理算法存在的内存碎片问题
      • 提高了吞吐量(不再需要找到合适的内存空间)
    • 缺点:
      • STW时间不能确定,收到复制量的影响
    • 垃圾收集器:Serial Old、Parallell Old

Java对象从年轻代晋升到老年代的过程?

  1. 首先,对象分配在Eden中

  1. 当eden区满了就会触发Minor GC,对象复制到Survivor From(S0)区域,然后清除Eden区对象


3. 当Survivor区也有数据了,在Minor GC时,它们会被复制到Survivor To(S1)区域,连同Eden区存活的对象,并且年龄+1.

  1. 后续就不断重复第三部,但是Survivor From(S0)区域和Survivor To(S1)区域会切换

  1. 当达到设置的晋升(-XX:MaxTenuringThreshold)年龄,将从Survivor移动到老年代

  1. 随着次要 GC 的不断发生,对象将继续被提升到老年代。并最终触发老年代GC

  1. -XX :PretenureSizeThreshold配置,直接晋升老年代
    这个可以配置阈值,当对象超过这个字节大小,将直接在老年代创建该对象。

  2. 动态年龄相关
    survivor区大小、survivor区目标使用率(-XX:TargetSurvivorRatio)、晋升年龄阈值(-XX:MaxTenuringThreshold),JVM会动态的计算tenuring threshold的值。一旦对象年龄达到了tenuring threshold就会晋升到老年代。

  • 官网链接:
  • https://www.oracle.com/webfolder/technetwork/Tutorials/obe/java/gc01/index.html
  • https://www.oracle.com/technical-resources/articles/javame/garbagecollection2.html

CMS垃圾回收的过程

2024-02-06T17:00:36.024+0800: 112904.916: [GC (Allocation Failure) 2024-02-06T17:00:36.025+0800: 112904.917: [ParNew: 1549196K->15440K(1731712K), 0.0153275 secs] 3004250K->1471195K(3655808K), 0.0158411 secs] [Times: user=0.08 sys=0.00, real=0.01 secs]
--
2024-02-06T08:28:06.223+0800: 82155.115: [GC (CMS Initial Mark) [1 CMS-initial-mark: 1770925K(1924096K)] 1796139K(3655808K), 0.0091685 secs] [Times: user=0.04 sys=0.01, real=0.01 secs]
2024-02-06T08:28:06.232+0800: 82155.124: [CMS-concurrent-mark-start]
2024-02-06T08:28:06.431+0800: 82155.323: [CMS-concurrent-mark: 0.199/0.199 secs] [Times: user=0.52 sys=0.01, real=0.20 secs]
2024-02-06T08:28:06.431+0800: 82155.323: [CMS-concurrent-preclean-start]
2024-02-06T08:28:06.441+0800: 82155.333: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2024-02-06T08:28:06.441+0800: 82155.333: [CMS-concurrent-abortable-preclean-start]
2024-02-06T08:28:09.672+0800: 82158.564: [CMS-concurrent-abortable-preclean: 3.023/3.231 secs] [Times: user=4.27 sys=0.12, real=3.23 secs]
2024-02-06T08:28:09.676+0800: 82158.568: [GC (CMS Final Remark) [YG occupancy: 790387 K (1731712 K)]2024-02-06T08:28:09.676+0800: 82158.568: [GC (CMS Final Remark) 2024-02-06T08:28:09.677+0800: 82158.569: [ParNew: 790387K->8346K(1731712K), 0.0106311 secs] 2561312K->1779760K(3655808K), 0.0111525 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2024-02-06T08:28:09.688+0800: 82158.580: [Rescan (parallel) , 0.0061328 secs]2024-02-06T08:28:09.694+0800: 82158.586: [weak refs processing, 0.0272328 secs]2024-02-06T08:28:09.721+0800: 82158.613: [class unloading, 0.0732422 secs]2024-02-06T08:28:09.795+0800: 82158.686: [scrub symbol table, 0.0244528 secs]2024-02-06T08:28:09.819+0800: 82158.711: [scrub string table, 0.0027189 secs][1 CMS-remark: 1771413K(1924096K)] 1779760K(3655808K), 0.1717571 secs] [Times: user=0.25 sys=0.00, real=0.17 secs]
2024-02-06T08:28:09.849+0800: 82158.741: [CMS-concurrent-sweep-start]
2024-02-06T08:28:10.477+0800: 82159.369: [CMS-concurrent-sweep: 0.625/0.629 secs] [Times: user=0.90 sys=0.05, real=0.63 secs]
2024-02-06T08:28:10.478+0800: 82159.370: [CMS-concurrent-reset-start]
2024-02-06T08:28:10.480+0800: 82159.372: [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

  • 初始标记(CMS Initial Mark):会STW,所有跟可达的对象都会标记

    • [1 CMS-initial-mark: 1770925K(1924096K)]:1924096K老年代容量,1770925K时触发GC
  • 并发标记(CMS-concurrent-mark):初始标记阶段标记的对象,在这一阶段中标记这些对象可达的对象

    • [CMS-concurrent-mark: 0.199/0.199 secs]:前0.199总时间,后0.199挂起时间

  • 并发预清理(CMS-concurrent-preclean):减轻重标记的负担。扫描变动的,新晋升的,上一个阶段发生变动的对象,JVM会标记堆的这个区域为Dirty Card
    • [CMS-concurrent-preclean: 0.009/0.009 secs]:前0.009运行时间,后0.009总挂起时间

  • 可中断并发预清理(CMS-concurrent-abortable-preclean):和上面一致,但是这个阶段是可以中断的。

  • 重新标记(CMS Final Remark):会STW,重新扫描 CMS 堆中任何残留的更新对象,从根开始回溯,并处理引用对象。

    • [GC (CMS Final Remark) [YG occupancy: 790387 K (1731712 K)]2024-02-06T08:28:09.676+0800: 82158.568: [GC (CMS Final Remark) 2024-02-06T08:28:09.677+0800: 82158.569: [ParNew: 790387K->8346K(1731712K), 0.0106311 secs] 2561312K->1779760K(3655808K), 0.0111525 secs] [Times: user=0.06 sys=0.00, real=0.01 secs][Rescan (parallel) , 0.0061328 secs]2024-02-06T08:28:09.694+0800: 82158.586: [weak refs processing, 0.0272328 secs]2024-02-06T08:28:09.721+0800: 82158.613: [class unloading, 0.0732422 secs]2024-02-06T08:28:09.795+0800: 82158.686: [scrub symbol table, 0.0244528 secs]2024-02-06T08:28:09.819+0800: 82158.711: [scrub string table, 0.0027189 secs][1 CMS-remark: 1771413K(1924096K)] 1779760K(3655808K), 0.1717571 secs] [Times: user=0.25 sys=0.00, real=0.17 secs]
      2024-02-06T08:28:09.849+0800: 82158.741: [CMS-concurrent-sweep-start]
      • YG occupancy: 790387 K (1731712 K):年轻代当前占用790387 K,年轻代总占用1731712 K
      • [ParNew: 790387K->8346K(1731712K), 0.0106311 secs],parNew清除后8346K
      • [Rescan (parallel) , 0.0061328 secs]:整堆重新扫描
      • [weak refs processing, 0.0272328 secs]:处理弱引用
      • [class unloading, 0.0732422 secs]:卸载未使用的类
      • [scrub symbol table, 0.0244528 secs]:清理符号表和字符串表,这些表存储着类级元数据和内部化字符串。

  • 并发清除(CMS-concurrent-sweep):开始并发清除

  • 重置阶段(CMS-concurrent-reset):重置cms数据,以便开启新的一次

  • 参考博客:

    • https://poonamparhar.github.io/understanding_cms_gc/
    • https://files.mdnice.com/user/10673/6ff75eb4-1e36-47ad-8b19-c80c1dbe488a.png

什么是Stop The World ? 什么是 OopMap ?什么是安全点?

  • Stop The World:是指JVM在执行垃圾回收时,会暂停应用程序的所有线程,直到垃圾回收完成为止。 在这个过程中,应用程序无法响应任何请求,所有的线程都会被挂起。

  • OopMap:是指对象指针映射表,在Java虚拟机中用于存储对象引用的位置和类型信息。
  • SafePoint:是在代码执行过程中的一些特殊位置,当线程执行到这些位置的时候,线程可以暂停。

GC日志的real、user、sys是什么意思?

  • real:程序从开始到结束所用的时钟时间。这个时间包括其他进程使用的时间片和进程阻塞的时间(比如等待 I/O 完成)。

  • user:进程执行用户态代码(核心之外)所使用的时间。这是执行此进程所使用的实际 CPU 时间,其他进程和此进程阻塞的时间并不包括在内。在垃圾收集的情况下,表示 GC 线程执行所使用的 CPU 总时间。

  • sys:进程在内核态消耗的 CPU 时间,即在内核执行系统调用或等待系统事件所使用的 CPU 时间。

  • 参考博客:

    • https://cloud.tencent.com/developer/article/1491229
    • https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1?lq=1

生产问题

怎么看死锁的线程?

  • 首先,先看最近提交记录,上过哪些需求,来确定范围。一般很好排查
  • 如果不行,现在我比较习惯用arthas
    • thread -b 可以查看当前阻塞的线程信息

  • thread {id} 可以答应堆栈信息
 public static void main(String[] args) {

      Object lock1 = new Object();
      Object lock2 = new Object();
        new Thread(() -> {
            synchronized (lock1) {
                sleep(2000);
                synchronized (lock2) {
                    System.out.println("myThread1 over");
                }
            }
        },"myThread1").start();

        new Thread(() -> {
            synchronized (lock2) {
                sleep(2000);
                synchronized (lock1) {
                    System.out.println("myThread1 over");
                }
            }
        },"myThread2").start();
    }

    private static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  • 参考链接:https://arthas.aliyun.com/doc/thread.html

对于JDK自带的监控和性能分析工具用过哪些?

之前写过,这里就不重复了链接

频繁Minor GC怎么办?

出现这个问题主要是年轻代设置的不大合理,设置的过于小了。

这样会导致Eden区和Survivor区很快被填满,动态年龄导致过早晋升。

可以通过jstat查看年轻态使用情况来判断

频繁Full GC怎么办?

这个最常见的就是以下两种状况

  • 过早晋升导致的
  • 内存泄漏

过早晋升可以结合频繁MinorGC一起判断

如果是内存泄漏,频繁FullGC之外,还伴随一个现象,FullGC时间过长,这时候需要把内存dump下来,放入分析工具(我常用JProfile)来进行分析

我之前的博客《Hutool:WeakCache导致的内存泄漏》就是这个场景

假如生产环境CPU占用过高,请谈谈你的分析思路和定位。

根据我定位的众多生产问题经验,尤其是cpu飙高问题,

  1. 首先先看是否持续,不持续多半是物理机出了点问题。
  2. 查看是否发版本后,基本上就是代码问题,回滚定位代码,本地模拟即可
  3. 查看是否有异常请求,Ddos攻击等等。
  4. 通过top命令是否是java进程。
  5. 如果cpu100%,且持续,判断是否可以回滚,然后进行重启,其次就是模拟这种场景,通过arthas查看cpu占用率高的线程,获取堆栈信息

内存飙高问题怎么排查?

  1. 分析是哪个进程占用内存
  2. 如果是java进程分析dump文件即可

有没有处理过内存泄漏问题?是如何定位的?

我之前的博客《Hutool:WeakCache导致的内存泄漏》

有没有处理过内存溢出问题?

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xx/xx正常项目都设置了这个参数,直接分析堆栈即可,OOM更容易,分析大对象即可

什么情况会造成元空间溢出?

默认元空间没有限制,只有到达了-XX:MaxMetaspaceSize或者物理内存不足的了才会造成元空间OOM

什么时候会造成堆外内存溢出?

当年排查swap的时候,阴差阳错分析过堆外内存。所以对这块源码有点影响

存在泄露有2个地方,1个是java自带的api,1个是netty的。

  • java自带的api
    同时这个也是arthas扫描的direct的来源


内存溢出的源码,-XX:MaxDirectMemorySize限制了这个,且达到了,会造成溢出

    private static boolean tryReserveMemory(long size, int cap) {

        // -XX:MaxDirectMemorySize limits the total capacity rather than the
        // actual memory usage, which will differ when buffers are page
        // aligned.
        long totalCap;
        while (cap <= maxMemory - (totalCap = totalCapacity.get())) {
            if (totalCapacity.compareAndSet(totalCap, totalCap + cap)) {
                reservedMemory.addAndGet(size);
                count.incrementAndGet();
                return true;
            }
        }

        return false;
    }
  • Netty
    还有一个是Netty的,当设置了io.netty.maxDirectMemory也会造成OOM

SWAP会影响性能么?

之前博客分析过:《生产问题复盘!Swap对GC的影响》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

桌面文件删除了怎么恢复?恢复文件,3个小秘诀!

在日常使用电脑的过程中&#xff0c;很多用户会习惯性的将文件保存在电脑桌面上。因此不可避免会出现桌面文件丢失的情况。这或许会对我们的工作和学习造成影响。 桌面文件删除了怎么恢复&#xff1f;如果你还没有掌握正确的恢复方法。记得继续往下看&#xff0c;下文有实用的…

C语言—数组一维(含冒泡排序)

1.用数组存储10个整型数&#xff0c;通过键盘输入一个数&#xff0c;找出该数在数组中的下标值&#xff1b; /*1.用数组存储10个整型数&#xff0c;通过键盘输入一个数&#xff0c;找出该数在数组中的下标值&#xff1b;*/#include <stdio.h>int main(void) {int nums[…

C图书信息管理系统 代码+报告

C图书信息管理系统 背景&#xff1a; 在当今信息时代&#xff0c;图书信息管理系统成为图书馆和书店等组织中不可或缺的一部分。随着图书数量的增加和信息化水平的提高&#xff0c;传统的手工管理方式已经无法满足快速、高效、精确的信息管理需求。因此&#xff0c;设计和实现一…

【STM32 物联网】AT指令的介绍

文章目录 前言一、什么是AT指令二、使用AT指令需要注意什么 三、AT指令的分类和提示信息3.1 AT指令的分类3.2 是否保存到Flash的含义3.3 提示信息 总结 前言 本系列STM32物联网使用的为esp8266-01S作为通信模块 在物联网&#xff08;IoT&#xff09;应用中&#xff0c;通信模…

为何 Serverless 革命停滞不前(译)

原文&#xff1a;Bernard Brode - 2020.10.12 主要观点 近几年来&#xff0c;有人预测 Serverless 计算将带来一种全新的计算时代&#xff0c;这种时代的应用程序无需操作系统即可运行。我们被告知这种框架将解决许多可扩展性问题。然而&#xff0c;现实并非如此。尽管许多人…

linux系统Grafana关联zabbix显示

Grafana关联zabbix 服务器下载浏览器配置开启zabbix插件配置zabbix数据源可视化Zabbix数据 服务器下载 grafana-cli plugins list-remote grafana-cli plugins list-remote|grep -i zabbix grafana-cli plugins install alexanderzobnin-zabbix-appsystemctl restart grafana-…

Sora爆火,普通人的10个赚钱机会

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通…

python-产品篇-游戏-成语填填乐

文章目录 准备代码效果 准备 无需其他文件&#xff0c;复制即用 代码 import random list["春暖花开","十字路口","千军万马","白手起家","张灯结彩","风和日丽","万里长城","人来人往",&…

Java集合篇之深入解析LinkedList

写在开头 作为ArrayList的同门师兄弟&#xff0c;LinkedList的师门地位逊色不少&#xff0c;除了在做算法题的时候我们会用到它之外&#xff0c;在实际的开发工作中我们极少使用它&#xff0c;就连它的创造者都说&#xff1a;“I wrote it&#xff0c;and I never use it”&am…

ESP32-Cam学习(1)——拍摄第一张照片

1.开发板介绍 使用的ESP32-Cam实物图为&#xff1a; 在某宝可以轻易买到。它分为主板&#xff0c;和底板。底板的主要功能是供电、程序下载等等。主板才是ESP32芯片的核心。 2.固件烧录 使用摄像头之前&#xff0c;需要给ESP32刷入支持摄像头的固件库&#xff0c;其下载地址为…

【DSP】ti和SYS/BIOS的printf

1. 引入 目的是在CCS中对printf进行重定向。关键是对fputc和fputs的重写。由下图可知&#xff0c;在sys/bios中的printf函数&#xff0c;会调用fputc打印一般的字符&#xff0c;会调用fputs打印转义字符得到的新的字符串。 2. 改写 首先&#xff0c;根据实际情况&#xff0…

一文了解Web3.0真实社交先驱ERA

Web2时代&#xff0c;少数科技巨头垄断了全球近60亿人口的网络社交数据&#xff0c;并用之为自己牟利&#xff0c;用户无法掌控个人数据&#xff0c;打破该局面逐渐成为共识&#xff0c;于是&#xff0c;不少人看到了Web3社交赛道蕴含的巨大机遇&#xff0c;标榜着去中心化和抗…

jmeter-11数据批量生成(向数据库批量插入数据)

文章目录 场景连接数据库添加循环控制器计数器新建JDBC请求运行结果运行前数据库数据为空运行后数据库多了十条数据场景 当你需要造数据的时候,比如注册20个新用户,这个时候可以使用jmeter与数据库连接,向数据库批量插入数据 连接数据库 具体连接方式:详见《jmeter-07jm…

多线程---创建线程

1.概述 多线程是指从软件或者硬件上实现多个线程并发执行的技术。线程是程序中独立运行的程序片段&#xff0c;每个线程都有独立的执行流程&#xff0c;可以在同一时间内执行不同的任务。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程&#xff0c;进而提…

SITLE24V2BNQ-3/TR瞬态电压抑制器

SITLE24V2BNQ是一种瞬态电压抑制器&#xff0c;设计用于保护两个汽车控制器区域 网络(CAN)母线不受ESD等瞬变造成的损坏。 SITLE24V2BNQ采用SOT-23封装。标准产品不含铅和卤素。

openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优

文章目录 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优222.1 前提条件222.2 内存相关参数设置222.3 网络相关参数设置222.4 I/O相关参数设置 openGauss学习笔记-222 openGauss性能调优-系统调优-操作系统参数调优 在性能调优过程中&#xff0c;可以根据…

第6个-滚动动画

Day 6 - Scroll Animation 1. 演示效果 2. 分析思路 布局 所有的内容进行水平垂直居中&#xff0c;可以使用**margin:0 auto;&#xff0c;也可以使用flex**布局&#xff1a; body {background-color: #efedd6;display: flex;flex-direction: column;justify-content: center…

计算机服务器中了_locked勒索病毒怎么办?Encrypted勒索病毒解密数据恢复

随着网络技术的不断发展&#xff0c;数字化办公已经成为企业生产运营的根本&#xff0c;对于企业来说&#xff0c;数据至关重要&#xff0c;但网络威胁无处不在&#xff0c;近期&#xff0c;云天数据恢复中心接到很多企业的求助&#xff0c;企业的计算机服务器遭到了_locked勒索…

GPT4微信机器人部署,集成gpt4问答、midjourney以及新闻等联网功能,免费可添加机器人成为自己专属助理

GPT问答和midjourney作为AI届两大亮点&#xff0c;都各自有官方体验方式。 同时&#xff0c;也有很多大神搭建了各类软件、平台供用户体验使用。 但是如果同时将GPT问答和midjourney集合到日常最常使用的微信呢&#xff1f; 打造一个微信机器人&#xff0c;不仅自己可以随时…

Halcon 相机标定

文章目录 算子单相机标定单相机标定畸变的矫正 算子 gen_caltab 生成标定文件 gen_caltab(::XNum,YNum,MarkDist,DiameterRatio,CalTabDescrFile,CalTabPSFile :) 算子来制作一个标定板XNum 每行黑色标志圆点的数量。YNum 每列黑色标志圆点的数…