A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
摘要
A公司的面经
- JVM的类加载的过程是怎么样的?
- 双亲委派模型的优点和缺点?
- 产生fullGC的情况有哪些?
- spring的动态代理有哪些?区别是什么?
- 如何排查CPU使用率过高?
JVM的类加载的过程是怎么样的?
这个问题有些抽象,是指要说出具体步骤,还是要深入每一步的细节?再次确认一下范围,给出的回答是,你自己了解多少就说多少。这就有意思,那我就凭自己的语言进行总结发挥了。
简述
类加载,是指JVM将.class文件的数据加载到内存中,并进行校验、解析以及初始化等一系列操作后,最终生成可被JVM直接使用的数据的过程。
解释
我们知道一个类在JVM的生命周期大致可以分为7个阶段:加载、验证、准备、解析、初始化、使用、卸载。
类加载的过程,主要就是类生命周期的前5个阶段,所以类加载的主要步骤为:
加载、验证、准备、解析、初始化。
因为【验证】、【准备】、【解析】有时候被统一称为链接阶段,因此有时候类加载也会被分三个步骤:加载、链接、初始化。

加载(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 = 10
static {
a = 20; // 最终 a = 20
}
}
双亲委派模型的优点和缺点?
Java应用是由 启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)、应用程序类加载器(Application Class Loader),这三类加载器互相配合来完成加载的,如果有自定义的类加载器,会先执行自定义的类加载器。
各种的类加载器之间的层次关系被称为类加载器的“双亲委派模型(Parents Delegation Model)”。

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。
双亲委派模型的工作过程
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶端的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载才会尝试自己去完成加载。
双亲委派模型的优点
- 避免类的重复加载。确保了不同类加载器加载的相同类是同一个实例,避免类型冲突(如
java.lang.Object的唯一性)。 - 防止恶意代码篡改核心类,以及避免因类版本不一致导致的兼容性问题。例如,攻击者无法通过自定义类加载器替换
java.lang.String为恶意实现,从而保障JVM运行安全。 - 提高类加载效率。通过层级委托机制,减少重复搜索类路径(ClassPath)的次数。类加载器只需尝试一次父类加载器的加载,若失败再自行加载,避免了全盘扫描,提升性能。
双亲委派模型的缺点
- 限制自定义类的动态更新。一旦类被父类加载器加载(如BootStrapClassLoader),即使类文件被修改,子类加载器也无法重新加载该类。
场景:
在热部署(Hot Deployment)或插件化系统中,需要动态更新类时,双亲委派机制会阻碍实现。
解决方案:
打破双亲委派机制:通过自定义类加载器绕过父类加载器,直接加载新版本类(例如Tomcat的WebAppClassLoader)。
使用模块化框架:如OSGi,通过隔离类加载器实现模块的独立更新。 - 子类加载器加载的类无法被父类加载器访问(单向依赖)。
场景:
在分布式系统中,可能需要跨类加载器共享数据,但父类加载器无法直接调用子类加载器加载的类。
解决方案:
通过接口或抽象类设计:将公共方法定义为接口,由父类加载器加载接口,子类加载器实现具体逻辑。
使用共享类路径:将需要共享的类放在父类加载器的类路径中。 - 类的可见性受限
子类加载器加载的类无法被父类加载器访问(单向依赖)。
场景:
在分布式系统中,可能需要跨类加载器共享数据,但父类加载器无法直接调用子类加载器加载的类。
解决方案:
通过接口或抽象类设计:将公共方法定义为接口,由父类加载器加载接口,子类加载器实现具体逻辑。
使用共享类路径:将需要共享的类放在父类加载器的类路径中。
产生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 重新平衡内存布局。
分布式缓存框架主动触发
某些分布式缓存框架(如 Ehcache、Redis 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' 19327
4b7f
接下来就可以通过jstack来查看栈信息
jstack 41846 |grep -A 200 4b7f
"main" #1 prio=5 os_prio=0 tid=0x00007f8a8c000000 nid=0x3048 runnable [0x00007f8a9c000000]
java.lang.Thread.State: RUNNABLE
at com.jimoer.app.CPUSpikeDemo.simulation(CPUSpikeDemo.java:12)
at com.jimoer.app.CPUSpikeDemo.main(CPUSpikeDemo.java:18)
通过输出的栈信息日志,可以看到,是CPUSpikeDemo这个类的第18行可能有问题。
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?的更多相关文章
- 性能测试分析过程(二)cpu 使用率过高的分析方法
Linux 系统下 cpu 使用率过高的分析方法 1.通过 top 命令可以很明显查看出哪个进程耗cpu比较高 2. ps -mp 25147-o THREAD,tid,time\top -Hp pi ...
- CPU使用率过高分析方法
项目过程中发现,应用服务器经常会出现CPU使用率较高的情况,需要定位出具体代码问题. 1.用top命令,根据CPU使用率排序,找出消耗cpu最高的进程 2.找出该进程下消耗CPU最高的线程(命令:to ...
- 06 案例篇:系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?
上一节我讲了 CPU 使用率是什么,并通过一个案例教你使用 top.vmstat.pidstat 等工具,排查高 CPU 使用率的进程,然后再使用 perf top 工具,定位应用内部函数的问题.不过 ...
- 06讲案例篇:系统的CPU使用率很高,但为啥却找不到高CPU的应用
小结 碰到常规问题无法解释的 CPU 使用率情况时,首先要想到有可能是短时应用导致的问题,比如有可能是下面这两种情况. 第一,应用里直接调用了其他二进制程序,这些程序通常运行时间比较短,通过 top ...
- 排查tomcat服务器CPU使用率过高
tomcat要运行依赖于JDK,tomcat服务器的CPU使用率过高,大多都是因为部署的web程序的问题. 一.现象描述 在一次线上环境,前台访问页面的速度越来越慢,从浏览器F12中看到发出的请求都是 ...
- 性能分析(3)- 短时进程导致用户 CPU 使用率过高案例
性能分析小案例系列,可以通过下面链接查看哦 https://www.cnblogs.com/poloyy/category/1814570.html 系统架构背景 VM1:用作 Web 服务器,来模拟 ...
- kubelet CPU 使用率过高问题排查
kubelet CPU 使用率过高问题排查 问题背景 客户的k8s集群环境,发现所有的worker节点的kubelet进程的CPU使用率长时间占用过高,通过pidstat可以看到CPU使用率高达100 ...
- 4 系统的 CPU 使用率很高,但为啥却找不到高 CPU的应用?
上一节讲了 CPU 使用率是什么,并通过一个案例教你使用 top.vmstat.pidstat 等工具,排查高 CPU 使用率的进程,然后再使用 perf top 工具,定位应用内部函数的问题.不过就 ...
- CPU使用率过高怎么办
实际上前文中关于CPU使用率过高如何通过各种工具获得相关的热点进程.那么进程有了,那得疑惑到底哪个哪段代码导致了这个进程成为热点呢? 如果在调试阶段,可以使用gdb中断运行,但是在生产环境肯定不行.L ...
- 服务器CPU使用率过高排查与解决思路
发现服务器的cpu使用率特别高 排查思路: -使用top或者mpstat查看cpu的使用情况# mpstat -P ALL 2 1Linux 2.6.32-358.el6.x86_64 (linux— ...
随机推荐
- C# 关于try-catch 异常无法被捕捉,解决方法
https://blog.csdn.net/weixin_30919571/article/details/102321538 有个处理方法在引发异常的发放上面加上 [System.Runtime.E ...
- # opengl 学习 之 07 lesson
opengl 学习 之 07 lesson 简介 OBJ的使用 link http://www.opengl-tutorial.org/uncategorized/2017/06/07/website ...
- java 连接远程的redis
简介 感觉还是挺麻烦的.尤其是mvvn 代码 import redis.clients.jedis.Jedis; public class TestPing { public static void ...
- Day10 备战CCF-CSP练习
Day10 题目描述 十滴水是一个非常经典的小游戏. 小 \(C\) 正在玩一个一维版本的十滴水游戏. 我们通过一个例子描述游戏的基本规则. 游戏在一个$ 1×c$ 的网格上进行,格子用整数$ x(1 ...
- RestCloud MQ集成平台,构建MQ与系统的桥梁
RestCloud MQ集成平台,实现对MQ消息的全面集成管控,通过消息订阅.发布.构建MQ与业务系统之间的桥梁. 一.MQ消息集成平台介绍1.消息订阅基于Web界面操作可以快速实现多种MQ(Kafk ...
- 在专业iPaaS厂商与大型软件企业间如何选择
一. 什么是iPaaS产品专业厂商? 专业iPaaS厂商指的是那些只专注于投入到iPaaS产品研发的创新型企业.这些专业公司不仅具备了丰富的行业集成经验和深厚的专业知识,更以其独特的视角和专注的态度, ...
- SciTech-BigDataAIML-LLM-Transformer Series系列: Word Embedding词嵌入详解: 用Corpus预训练出嵌入矩阵E→Input变长词序列→Onehot"词列向量"序列→矩阵乘"嵌入矩阵E"→Embedded"词列向量"序列"
SciTech-BigDataAIML-LLM-Transformer Series系列: Word Embedding词嵌入详解: 1. 用Corpus预训练出嵌入矩阵\(\large E\) \( ...
- 解决SQL server中提示对象名无效--九五小庞
产生SQL对象名无效的问题大多原因是由于数据迁移导致的,下面我们给出解决方法. 在使用数据库的过程中,经常会遇到数据库迁移或者数据迁移的问题,或者有突然的数据库损坏,这时需要从数据库的备份中直接恢复. ...
- 正版Win11纯净版文件夹无响应卡死的问题
有一些使用win11正式版系统的小伙伴反馈说,他在打开文件夹速度都很慢,甚至还会出现无响应卡死的情况,那么遇到这种情况应该怎么办呢?下面雨林木风官网小编就来简单的说说解决方法吧.过程简单,操作方便!一 ...
- Mysql查询语句执行流程?更新语句执行流程?
查询语句执行流程 查询语句的执行流程如下:权限校验.查询缓存.分析器.优化器.权限校验.执行器.引擎. 举个例子,查询语句如下: select * from user where id > 1 ...