A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?

news/2025/9/18 17:40:32/文章来源:https://www.cnblogs.com/jimoer/p/19099260

摘要

A公司的面经

  • JVM的类加载的过程是怎么样的?
  • 双亲委派模型的优点和缺点?
  • 产生fullGC的情况有哪些?
  • spring的动态代理有哪些?区别是什么?
  • 如何排查CPU使用率过高?

JVM的类加载的过程是怎么样的?

这个问题有些抽象,是指要说出具体步骤,还是要深入每一步的细节?再次确认一下范围,给出的回答是,你自己了解多少就说多少。这就有意思,那我就凭自己的语言进行总结发挥了。

简述

类加载,是指JVM将.class文件的数据加载到内存中,并进行校验、解析以及初始化等一系列操作后,最终生成可被JVM直接使用的数据的过程。

解释

我们知道一个类在JVM的生命周期大致可以分为7个阶段:加载、验证、准备、解析、初始化、使用、卸载
类加载的过程,主要就是类生命周期的前5个阶段,所以类加载的主要步骤为:
加载、验证、准备、解析、初始化
因为【验证】、【准备】、【解析】有时候被统一称为链接阶段,因此有时候类加载也会被分三个步骤:加载、链接、初始化
image

加载(Loading)

第一步,加载,主要是通过类的全限定名(如:java.lang.String)获取类的二进制字节流,将字节流转换为JVM运行时的数据结构,在堆中生成一个 java.lang.Class 对象,作为该类的访问入口。
触发方式:

  • ClassLoader.getSystemClassLoader().loadClass("com.jimoer.Test")
  • Class.forName("com.jimoer.Test") // 加载并初始化
  • 创建实例(new Test())、调用静态方法或访问静态字段。

验证(Verification)

主要是校验,.class文件的正确性。
校验类的正确性(文件格式,元数据,字节码,二进制兼容性),保证类的结构符合JVM规范。

准备(Preparation)

为类的 静态变量(static 字段)分配内存并设置 默认值
这里只初始化类变量,即static变量,所以都是在方法区里面进行分配内存的。而实例变量是会在对象实例化的时候进行初始化的,并在Java堆里分配内存。

解析(Resolution)

将常量池中的 符号引用 转换为 直接引用。
把类的符号引用转为直接引用(类或接口、字段、类方法、接口方法、方法类型、方法句柄和访问控制修饰符7类符号引用)。

初始化(Initialization)

执行类的 初始化逻辑(即 <clinit>() 方法),完成静态变量赋值和静态代码块的执行。

public class ClassInit {static int a = 10; // 准备阶段:a = 0;初始化阶段:a = 10static {a = 20; // 最终 a = 20}
}

双亲委派模型的优点和缺点?

Java应用是由 启动类加载器(Bootstrap Class Loader)扩展类加载器(Extension Class Loader)应用程序类加载器(Application Class Loader),这三类加载器互相配合来完成加载的,如果有自定义的类加载器,会先执行自定义的类加载器。
各种的类加载器之间的层次关系被称为类加载器的“双亲委派模型(Parents Delegation Model)”。
image

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。

双亲委派模型的工作过程

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶端的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载才会尝试自己去完成加载。

双亲委派模型的优点

  1. 避免类的重复加载。确保了不同类加载器加载的相同类是同一个实例,避免类型冲突(如java.lang.Object的唯一性)。
  2. 防止恶意代码篡改核心类,以及避免因类版本不一致导致的兼容性问题。例如,攻击者无法通过自定义类加载器替换java.lang.String为恶意实现,从而保障JVM运行安全。
  3. 提高类加载效率。通过层级委托机制,减少重复搜索类路径(ClassPath)的次数。类加载器只需尝试一次父类加载器的加载,若失败再自行加载,避免了全盘扫描,提升性能。

双亲委派模型的缺点

  1. 限制自定义类的动态更新。一旦类被父类加载器加载(如BootStrapClassLoader),即使类文件被修改,子类加载器也无法重新加载该类。
    场景:
    在热部署(Hot Deployment)或插件化系统中,需要动态更新类时,双亲委派机制会阻碍实现。
    解决方案
    打破双亲委派机制:通过自定义类加载器绕过父类加载器,直接加载新版本类(例如Tomcat的WebAppClassLoader)。
    使用模块化框架:如OSGi,通过隔离类加载器实现模块的独立更新。
  2. 子类加载器加载的类无法被父类加载器访问(单向依赖)。
    场景
    在分布式系统中,可能需要跨类加载器共享数据,但父类加载器无法直接调用子类加载器加载的类。
    解决方案
    通过接口或抽象类设计:将公共方法定义为接口,由父类加载器加载接口,子类加载器实现具体逻辑。
    使用共享类路径:将需要共享的类放在父类加载器的类路径中。
  3. 类的可见性受限
    子类加载器加载的类无法被父类加载器访问(单向依赖)。
    场景:
    在分布式系统中,可能需要跨类加载器共享数据,但父类加载器无法直接调用子类加载器加载的类。
    解决方案:
    通过接口或抽象类设计:将公共方法定义为接口,由父类加载器加载接口,子类加载器实现具体逻辑。
    使用共享类路径:将需要共享的类放在父类加载器的类路径中。

产生FullGC的情况有哪些?

JVM触发FullGC的情况比较复杂也比较多,这里只说一些常见的,不能保证包含了全部产生FullGC的情况。

老年代空间不足

老年代存储空间不足

当老年代不足以容纳新对象或新生代晋升的对象时,会触发 Full GC
大对象直接分配到老年代(通过 -XX:PretenureSizeThreshold 阈值)。
Survivor 区无法容纳所有存活对象(担保机制触发晋升到老年代)。

老年代连续空间不足

即使老年代总空间足够,但碎片化严重(如大量小对象释放后未合并),无法分配大对象时,也会触发 Full GC。

元空间内存不足

当元空间(Metaspace)存储类元数据的空间不足时,JVM 会尝试通过 Full GC 回收无用的类元数据(如卸载不再使用的类)。
若仍不足,则抛出 OutOfMemoryError: Metaspace

System.gc() 被显式调用

显式调用 System.gc() 会请求 JVM 执行 Full GC(可通过 -XX:+DisableExplicitGC 禁用)。

OOM 前的最后尝试

当内存不足错误(OOM)触发。
当 JVM 即将抛出 OutOfMemoryError(如堆内存不足 Java heap space 或元空间不足 Metaspace)时,会尝试通过 Full GC 回收垃圾,若仍失败则抛出 OOM

自适应内存管理策略

若 JVM 的自适应内存管理(如 -XX:+UseAdaptiveSizePolicy)动态调整堆内存时发现内存紧张,可能触发 Full GC 重新平衡内存布局。

分布式缓存框架主动触发

某些分布式缓存框架(如 EhcacheRedis Java 客户端)在检测到堆内存占用过高时,会主动触发 Full GC 回收缓存对象。
然而这种做法需谨慎使用,可能导致性能问题,可能引发性能抖动甚至 STW(Stop-The-World)时间过长。

其他特殊场景

内存泄漏

未被正确释放的对象(如未关闭数据库连接、线程池未关闭、ThreadLocal 未 remove())导致老年代持续增长,最终触发 Full GC。

晋升年龄阈值过低

若新生代对象晋升到老年代的年龄阈值(-XX:MaxTenuringThreshold)设置过小,对象过早进入老年代,可能加速老年代空间耗尽。

Spring使用的动态代理有哪些?区别是什么?

Spring框架中主要使用两种动态代理技术:JDK动态代理CGLIB动态代理

JDK动态代理

基于接口实现:通过Java自带的 java.lang.reflect.Proxy 类动态生成代理类,代理类会实现目标类所实现的所有接口。
通过反射调用目标方法(Method.invoke()),性能相对较低。

适用场景

目标类实现了至少一个接口(如Service层接口)。
适用于需要兼容接口扩展性的场景。

优点

代码简洁:无需引入额外依赖。
兼容性强:适合有接口的设计模式。

缺点

局限性:目标类必须实现接口,否则无法使用。
性能问题:基于反射调用,性能较低(尤其是高频调用时)
无法获取实现类方法上的注解:当目标类实现接口时,代理对象可能无法直接获取实现类方法上的注解

CGLIB动态代理

通过CGLIB库(Code Generation Library)动态生成目标类的子类,重写方法实现代理
直接调用父类方法(非反射),性能较高。
需要引入CGLIB库(如 cglib 或 spring-core)。
spring-boot现在默认是使用CGLIB动态代理。

适用场景

目标类未实现任何接口(如Controller层或第三方类)
需要代理final类或方法以外的普通类

优点

灵活性高:无需目标类实现接口
性能更高:直接调用方法,避免反射开销
支持更复杂的代理需求:如代理无接口类

缺点

依赖第三方库:需要引入CGLIB依赖
限制:无法代理final类或final方法(因为无法继承和重写)
生成代理类较慢:字节码生成过程比JDK动态代理稍慢

如何排查CPU使用率过高?

首先登录到服务器上,看一下具体情况。

定位进程

登录服务器,执行top命令,查看CPU占用情况。

PID    COMMAND      %CPU  TIME     #TH   #WQ  #PORT MEM    PURG   CMPRS  PGRP  PPID  STATE
41846  java        130.4  04:36:58 14/1  5    1695+ 618M-  6356K  99M-   41846 1     running
18122  top          7.5   00:04.68 1/1   0    32    4872K  0B     0B     18122 18106 running

通过Top命令,可以看到,占用CPU最高的是PID为41846的这个java进程。

定位线程

由于 Java 程序是单进程多线程模型,因此需要进一步定位具体是哪个线程的CPU占用最高。
同样是使用top命令:top -Hp 41846

PID    COMMAND   %CPU TIME     #TH  #WQ  #POR MEM   PURG CMPRS PGRP  PPID
19327  java       130  00:12.59 30   1    141  154M  0B   123M- 41846 41846

top -Hp 41846命令可以看到,当前进程下,线程ID为19327的占用CPU最高。

定位代码

首先将线程ID转成16进制

printf '%x\n' 193274b7f

接下来就可以通过jstack来查看栈信息

jstack 41846 |grep -A 200 4b7f
"main" #1 prio=5 os_prio=0 tid=0x00007f8a8c000000 nid=0x3048 runnable [0x00007f8a9c000000]java.lang.Thread.State: RUNNABLEat com.jimoer.app.CPUSpikeDemo.simulation(CPUSpikeDemo.java:12)at com.jimoer.app.CPUSpikeDemo.main(CPUSpikeDemo.java:18)

通过输出的栈信息日志,可以看到,是CPUSpikeDemo这个类的第18行可能有问题。

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

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

相关文章

redis-hash类型参数基本命令

redis-hash类型参数基本命令redis存储数据的value可以是hash类型的,也称之为hash表,字典等。hash表就是一个map,由key-value组成。 我们把hash表的key称为field,值称为value。注意:redis的hash表的field和value都…

Alternating Subsequence

CF1343C Alternating Subsequence 题目描述 回忆一下,如果序列 \(b\) 是序列 \(a\) 的一个子序列,那么 \(b\) 可以通过从 \(a\) 中删除零个或多个元素(不改变剩余元素的顺序)得到。例如,如果 \(a=[1, 2, 1, 3, 1,…

白鲸开源“创客北京2025”再摘殊荣,聚焦Agentic AI时代数据基础设施建设

近日,“创客北京2025”创新创业大赛海淀区级赛圆满落幕,经过最终比拼,北京白鲸开源科技有限公司凭借 「Agentic AI时代下的数据基础设施平台」(白鲸数据集成调度平台/WhaleStudio) 脱颖而出,荣获企业组二等奖。近…

深入解析:大模型-Transformer原理与实战篇

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

python基础-公共操作

数据类型间公共支持的操作符运算: + ,* ,in , not in‘+’ :支持的容器类型 字符串、列表、元组 ,实现两个容器的合并‘*’ : 支持的容器类型 字符串、列表、元组, 赋值容器内容str1 = q str1* 5 =qq…

天翼云第九代弹性云主机:让每一次计算快人一步

随着数字化转型进程不断深入,云计算已成为推动千行百业智能化升级的核心引擎。弹性计算服务凭借其灵活扩展、高可用和高性能等特点,正持续为企业提供关键基础设施支持。面对日益复杂的业务场景与持续增长的计算需求,…

若依(RuoYi)框架漏洞总结

0x01 特征 绿若依 icon_hash=”706913071”蓝若依 icon_hash=” -1231872293”0x02 漏洞 弱口令 用户:admin ruoyi druid 密码:123456 admin druid admin123 admin888若依前台默认shiro key命令执行漏洞…

第一次个人项目作业_论文查重

第一次项目作业这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477这…

2025年版《中科院期刊分区表》与2023年版对比表,附名单可直接查阅

2025年版《中科院期刊分区表》与2023年版相比,主要有以下几个变化‌: ‌1、发布时间提前‌:2025年版分区表从12月提前至3月发布,与投稿周期同步,学者可以尽早锁定期刊最新分区,避免“投稿后降区”的风险‌。 ‌2…

对马岛之魂

护身符 稻荷神护身符----增加资源的获取 aa

2019年双因素认证最佳实践指南

本文深入探讨2019年双因素认证的正确实现方式,对比TOTP与WebAuthn技术优劣,分析用户行为模式,并提供实际部署建议,帮助开发者构建更安全的认证系统。2019年正确实现双因素认证 - Trail of Bits博客 自3月起,Trail…

oracle 删除重复数据

delete hpas_index_data_source swhere s.id in (select idFROM (SELECT t1.*,ROW_NUMBER() OVER(PARTITION BY t1.indexid, t1.doctor_id, t1.start_date, t1.vals ORDER BY t1.rowid) as rnFROM hpas_index_data_sou…

Account Kit(华为账号服务)再进化,开发者接入效率飙升!

Hi 各位开发者朋友~👋 为持续优化开发体验,提升集成效率,Account Kit接入体验再升级,助力构建更流畅、更安全的登录体验,让开发效率火力全开!😎 【体验升级】华为账号相关权益申请入口统一迁移至AGC华为账号…

Codeforces Round 1051 (Div. 2) D题启发(DP

题目简述 需要找到所有最长单调递减子序列长度不超过2的子列个数,做法是dp。 状态记录 我们不必理会题解中乱七八糟的定义,只需要知道他事实上就是模拟了当我们拿到一个已知数列时贪心的过程,把我们计算最长单调递减…

[踩坑劝退]批量生成 grafana dashboard 的技术

[踩坑劝退]批量生成 grafana dashboard 的技术作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客 zhihu Github 公众号:一本正经的瞎扯最近想要一个功能:把 VictoriaMetrics 采集的数据,自动变…

Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录

本文记录了在 Ubuntu 22.04 上部署 Apache DolphinScheduler(伪集群模式)的完整过程,涵盖环境准备、安装配置、数据库初始化、用户创建及服务启动等步骤。适合个人学习、功能验证或测试使用。本文记录了在 Ubuntu 2…

关于proxmox 制作虚拟机模板的动态dhcp问题

背景 proxmox 制作ubuntu24.04 的模板,虽然已经设置了dhcp,启动了ip还是会附带之前的信息。为了解决这个问题。 如果要保证 Ubuntu 云镜像克隆后每台机器都自动生成唯一的 machine-id 和 DHCP 客户端 ID(避免 IP 冲…

Oracle清理:如何安全删除trace, alert和archivelog文件?

Oracle清理:如何安全删除trace, alert和archivelog文件?Oracle 数据库运行时文件清理指南 背景:公司阿里云数据库主机,由于长期运行空间告急,通知各项目组多次要求转移数据库空间文件进展缓慢,为保证空间占满影响…

软件工程个人项目

软件工程个人项目3123004548软件工程个人项目这个作业属于哪个课程 <https://edu.cnblogs.com/campus/gdgy/SoftwareEngineering2024>这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23Co…

学习道路道阻且长 希望自己坚持下去

本人是一名专升本的大三学生 现在专业是软件工程专业 从今天开始学习java 翻了一下资料 发现很多人建议从前端开始学习 在专科学习中 也学过相应的基础知识,不过遗忘程度可能有点严重。对于语言的基本语法掌握需要加强…