【Java开发】Java面试七大专题第5篇:4. 内存溢出,5. 类加载【附代码文档】

49 阅读1分钟

🏆🏆🏆教程全知识点简介:基础篇 1. 二分查找 2. 冒泡排序 7. ArrayList 8. Iterator 9. LinkedList 10. HashMap 1)基本数据结构 2)树化与退化 3)索引计算 4)put 与扩容 5)并发问题 11. 单例模式 并发篇 1. 线程状态 3. wait vs sleep 4. lock vs synchronized 虚拟机篇 1. JVM 内存结构 4. 内存溢出 5. 类加载 6. 四种引用 7. finalize 框架篇 1. Spring refresh 流程 2. Spring bean 生命周期 6. Spring 注解 7. SpringBoot 自动配置原理 数据库篇 1. 隔离级别 2. 快照读与当前读 3. InnoDB vs MyISAM 4. 索引 索引基础 5. 查询语句执行流程 6. undo log 与 redo log 7. 锁 缓存篇 1. Redis 数据类型 2. keys 命令问题 3. 过期 key 的删除策略 5. 缓存问题 6. 缓存原子性 7. LRU Cache 实现 分布式篇 1. CAP 定理 2. Paxos 算法 4. Gossip 协议 5. 分布式通用设计 6. 一致性 Hash(补充)


📚📚👉👉👉git仓库code.zip 直接get:   gitlab.com/yiqing112/b…    🍅🍅

✨ 本教程项目亮点

🧠 知识体系完整:覆盖从基础原理、核心方法到高阶应用的全流程内容
💻 全技术链覆盖:完整前后端技术栈,涵盖开发必备技能
🚀 从零到实战:适合 0 基础入门到提升,循序渐进掌握核心能力
📚 丰富文档与代码示例:涵盖多种场景,可运行、可复用
🛠 工作与学习双参考:不仅适合系统化学习,更可作为日常开发中的查阅手册
🧩 模块化知识结构:按知识点分章节,便于快速定位和复习
📈 长期可用的技术积累:不止一次学习,而是能伴随工作与项目长期参考


🎯🎯🎯全教程总章节


🚀🚀🚀本篇主要内容

4. 内存溢出

要求

  • 能够说出几种典型的导致内存溢出的情况

典型情况

  • 误用线程池导致的内存溢出
    • 参考 day03.TestOomThreadPool
  • 查询数据量太大导致的内存溢出
    • 参考 day03.TestOomTooManyObject
  • 动态生成类导致的内存溢出
    • 参考 day03.TestOomTooManyClass

5. 类加载

要求

  • 掌握类加载阶段
  • 掌握类加载器
  • 理解双亲委派机制

类加载过程的三个阶段

  1. 加载

    1. 将类的字节码载入方法区,并创建类.class 对象

    2. 如果此类的父类没有加载,先加载父类

    3. 加载是懒惰执行

Mkyong Java 教程

  1. 链接

    1. 验证 – 验证类是否符合 Class 规范,合法性、安全性检查
    2. 准备 – 为 static 变量分配空间,设置默认值
    3. 解析 – 将常量池的符号引用解析为直接引用
  2. 初始化

    1. 静态代码块、static 修饰的变量赋值、static final 修饰的引用类型变量赋值,会被合并成一个 <cinit> 方法,在初始化时被调用
    2. static final 修饰的基本类型变量赋值,在链接阶段就已完成
    3. 初始化是懒惰执行

验证手段

  • 使用 jps 查看进程号
  • 使用 jhsdb 调试,执行命令 jhsdb.exe hsdb 打开它的图形界面
    • Class Browser 可以查看当前 jvm 中加载了哪些类
    • 控制台的 universe 命令查看堆内存范围
    • 控制台的 g1regiondetails 命令查看 region 详情
    • scanoops 起始地址 结束地址 对象类型 可以根据类型查找某个区间内的对象地址
    • 控制台的 inspect 地址 指令能够查看这个地址对应的对象详情
  • 使用 javap 命令可以查看 class 字节码

代码说明

  • day03.loader.TestLazy - 验证类的加载是懒惰的,用到时才触发类加载
  • day03.loader.TestFinal - 验证使用 final 修饰的变量不会触发类加载

Spring Data JPA 文档

jdk 8 的类加载器

名称加载哪的类说明
Bootstrap ClassLoaderJAVA_HOME/jre/lib无法直接访问
Extension ClassLoaderJAVA_HOME/jre/lib/ext上级为 Bootstrap,显示为 null
Application ClassLoaderclasspath上级为 Extension
自定义类加载器自定义上级为 Application

双亲委派机制

Feign 文档

Vavr 函数式库

所谓的双亲委派,就是指优先委派上级类加载器进行加载,如果上级类加载器

  • 能找到这个类,由上级加载,加载后该类也对下级加载器可见
  • 找不到这个类,则下级类加载器才有资格执行加载

双亲委派的目的有两点

  1. 让上级类加载器中的类对下级共享(反之不行),即能让你的类能依赖到 jdk 提供的核心类

  2. 让类的加载有优先次序,保证核心类优先加载

对双亲委派的误解

Javatpoint 教程

下面面试题的回答是错误的

错在哪了?

  • 自己编写类加载器就能加载一个假冒的 java.lang.System 吗? 答案是不行。

  • 假设你自己的类加载器用双亲委派,那么优先由启动类加载器加载真正的 java.lang.System,自然不会加载假冒的

  • 假设你自己的类加载器不用双亲委派,那么你的类加载器加载假冒的 java.lang.System 时,它需要先加载父类 java.lang.Object,而你没有用委派,找不到 java.lang.Object 所以加载会失败

  • 以上也仅仅是假设。事实上操作你就会发现,自定义类加载器加载以 java. 打头的类时,会抛安全异常,在 jdk9 以上版本这些特殊包名都与模块进行了绑定,更连编译都过不了

代码说明

  • day03.loader.TestJdk9ClassLoader - 演示类加载器与模块的绑定关系

6. 四种引用

要求

  • 掌握四种引用

强引用

  1. 普通变量赋值即为强引用,如 A a = new A();

  2. 通过 GC Root 的引用链,如果强引用不到该对象,该对象才能被回收

OkHttp 文档

image-20210901111903574

软引用(SoftReference)

  1. 例如:SoftReference a = new SoftReference(new A());

  2. 如果仅有软引用该对象时,首次垃圾回收不会回收该对象,如果内存仍不足,再次回收时才会释放对象

  3. 软引用自身需要配合引用队列来释放

  4. 典型例子是反射数据

image-20210901111957328

弱引用(WeakReference)

  1. 例如:WeakReference a = new WeakReference(new A());

  2. 如果仅有弱引用引用该对象时,只要发生垃圾回收,就会释放该对象

SpringDoc OpenAPI

  1. 弱引用自身需要配合引用队列来释放

  2. 典型例子是 ThreadLocalMap 中的 Entry 对象

image-20210901112107707

虚引用(PhantomReference)

  1. 例如: PhantomReference a = new PhantomReference(new A(), referenceQueue);

  2. 必须配合引用队列一起使用,当虚引用所引用的对象被回收时,由 Reference Handler 线程将虚引用对象入队,这样就可以知道哪些对象被回收,从而对它们关联的资源做进一步处理

  3. 典型例子是 Cleaner 释放 DirectByteBuffer 关联的直接内存

image-20210901112157901

代码说明

  • day03.reference.TestPhantomReference - 演示虚引用的基本用法
  • day03.reference.TestWeakReference - 模拟 ThreadLocalMap, 采用引用队列释放 entry 内存

7. finalize

要求

  • 掌握 finalize 的工作原理与缺点

finalize

[SpotBugs 文档](spotbugs.github.