1. 为什么需要垃圾回收(GC)
- Java 对象在堆上动态分配,程序运行中不断创建对象,若无回收机制,内存会耗尽。
- GC 自动识别不可达对象并回收内存,降低对性能的影响。
2. JVM 堆与分代机制
- 堆(Heap) 始终分为新生代(短生命周期)和老年代(长生命周期)
- 方法区(存储类元数据):
- Java 8 前:永久代(PermGen,堆的一部分)存储类信息
- Java 8+:元空间(Metaspace,使用本地内存)替代永久代
- 分代基于经验:多数对象生命周期短,新生代 GC 频繁但快
一句话理解:JVM 参数就像汽车的调节旋钮,-X 调基础,-XX 调高级,掌握规则轻松玩转 JVM 调优!
| 参数类型 | 前缀 | 记忆口诀 | 典型用途 |
|---|---|---|---|
| 标准参数 | - |
通用配置 | -version, -cp |
| 扩展参数 | -X |
基础调优 | -Xms, -Xmx, -Xss |
| 高级参数 | -XX: |
高级特性 | -XX:+UseG1GC, -XX:+PrintGCDetails |
| 诊断参数 | -Xlog |
日志调试 | -Xlog:gc* |
在 JVM 里,一个 Java 对象到底长什么样?你的 class 文件、字段、继承关系,最后在堆里是怎么被组织的?这一篇文章用尽量平实的语言、清晰的结构,把 HotSpot 中对象的真实面貌完整讲清。
无论什么对象,它在堆里都由 三部分 组成:
[ 对象头 (Object Header) ]
[ 实例数据 (Instance Data) ]
[ 对齐填充 (Padding) ]
ConcurrentHashMap 是 Java 并发编程中的线程安全哈希表实现,通过分段锁、CAS无锁操作和原子计数机制实现高性能并发访问。
┌─────────────────────────────────────────────────────────┐
│ ConcurrentHashMap │
│ ┌─────────────┬─────────────┬─────────────┬──────────┐ │
│ │ Node[] table│ 链表 │ 红黑树 │ 计数统计 │ │
│ │ (桶数组) │ (链表头节点)│ (树化阈值8) │ (baseCount│ │
│ │ │ │ │ +counter)│ │
│ └─────────────┴─────────────┴─────────────┴──────────┘ │
└─────────────────────────────────────────────────────────┘
HashMap 是 Java 常用哈希表实现,用于存储键值对,底层基于数组 + 链表/红黑树结构,通过哈希函数快速定位,用链表/红黑树解决冲突,实现 O(1) 级别的平均操作效率。
HashMap 采用「数组 + 链表 + 红黑树」的分层存储结构,三者形成有机协作关系:
数组(哈希桶/table):作为底层基础容器,是 HashMap 的一级存储结构。数组下标通过 Key 的哈希值计算得出,实现 O(1) 时间复杂度的快速定位,是高效查询的基础。
链表(链表节点/Node):作为二级存储结构,用于处理数组中的哈希冲突。当不同 Key 计算出相同数组下标时,这些键值对会在数组同一位置形成链表结构,解决了哈希值碰撞问题。
红黑树(树节点/TreeNode):作为链表的升级结构,用于优化查询效率。当 链表长度超过阈值(默认 8)且数组长度 ≥ 64 时,链表会自动转换为红黑树,将查询时间复杂度从链表的 O(n) 优化至 O(log n),避免了长链表导致的性能瓶颈。
在Java企业级开发中,我们经常听到javax和jakarta两个包名。本文将详细介绍它们的历史背景、主要区别以及在实际项目中的应用。
com.sun.* 迁移到 javax.*SPI(Service Provider Interface)是Java提供的一种服务发现机制,用于在运行时动态加载和发现服务实现。它通过在classpath路径下的META-INF/services文件夹中配置接口的实现类,使得程序可以在运行时根据配置加载相应的实现。
SPI机制的核心思想是解耦接口和实现。通过配置文件的方式,将接口的实现类完全交给第三方来实现,而不需要在代码中硬编码具体的实现类。这种机制使得框架具有良好的扩展性,开发者可以根据需要提供不同的实现。
切面编程(AOP)是分布式系统中用于解耦横切关注点的核心技术,而动态代理正是AOP实现的底层引擎。在淘票票项目中,我们通过AOP结合动态代理实现了分布式锁的无侵入式集成,既让业务代码保持简洁,又解决了并发安全问题。
本文将从AOP思想出发,深入讲解JDK动态代理与CGLIB动态代理的原理,并结合淘票票的分布式锁实现,完整展示从原理到实践的全过程。
在传统的面向对象编程(OOP)中,业务逻辑通常按照“纵向”划分,例如支付、下单、退款等。但日志记录、事务控制、安全校验这些功能,会横向地出现在各个业务中:
在微服务架构里,高并发场景就像“早高峰的地铁站”——传统同步编程的“排队检票”模式很容易堵死,而响应式编程的“异步分流”模式能让系统更高效地处理请求。作为Java响应式编程的“基石”,Reactor框架基于Reactive Streams规范和Java 8特性(Lambda、CompletableFuture)实现,底层还整合了Netty的非阻塞IO能力,专门解决“高并发下的资源浪费”问题;而Spring Cloud Gateway则基于Reactor,把这种能力落地到网关层,帮我们处理路由、过滤等核心需求。今天就从基础到实战,把Reactor和Gateway的核心逻辑讲明白,重点解答“请求体只能读一次”“重新创建的Flux为何能重复订阅”这些高频坑。
在微服务架构中,网关是请求和响应的“必经之路”。处理响应体(如加密、校验、脱敏)是网关的高频需求,但很多开发者会困惑:“为什么我配置的响应过滤器,看似在控制器前执行?”“order值和执行阶段到底是什么关系?”更会疑惑:“Gateway的过滤器和Spring MVC的拦截器有什么区别?该怎么选?”
在对比过滤器之前,先明确两者的本质区别——定位不同决定了过滤器的设计逻辑不同:
| 框架 | 定位 | 核心场景 | 底层依赖 | 数据处理模式 |
|---|---|---|---|---|
| Spring Cloud Gateway | 微服务网关(跨服务) | 路由转发、跨服务鉴权、全局响应处理 | Netty(非阻塞IO) | 响应式流(Flux/Mono) |
| Spring MVC | 单体/服务内部控制器 | 服务内请求处理、接口级拦截 | Servlet API(阻塞IO) | 同步请求响应 |