Java

25 篇文章
线上CPU飙高如何排查?

线上CPU飙高如何排查?

本文详细介绍了如何排查和定位系统中高 CPU 使用情况,特别是针对 Java 进程的高 CPU 问题。主要步骤包括:首先通过 `top` 命令查看系统整体 CPU 使用情况,重点关注用户态和内核态的 CPU 占比;其次,使用 `top` 命令定位高 CPU 的 Java 进程,并记录其 PID;最后,通过 `top -H -p <PID>` 命令查看该进程下的线程 CPU 占用情况,并将高消耗线程的 TID 转换为 16 进制,使用 `jstack` 抓取该线程的堆栈信息,分析 `RUNNABLE` 状态的线程以确定 CPU 消耗原因。 常见的高 CPU 场景包括正则表达式灾难性回溯、频繁序列化大对象以及线程池积压等问题。文章还补充了线上应急排查的建议,推荐使用 `top + jstack` 组合,并提到在容器环境中如何进行排查。总结指出,CPU 飙高排查的核心是通过系统、进程、线程/堆栈三步递进,关键命令包括 `top`、`printf`、`jstack`,常见问题多为正则回溯、大对象序列化和线程池积压。

Redis 雪崩场景重现与解决方案

Redis 雪崩场景重现与解决方案

本文详细介绍了Redis雪崩问题的产生原因及解决方案。Redis雪崩是指大量缓存在同一时间失效,导致所有请求直接打到数据库,造成数据库压力骤增甚至宕机的现象。文章首先通过模拟1000个缓存key在10秒后同时失效,并使用多线程模拟高并发请求,重现了雪崩场景。结果显示,缓存失效后,10000个请求全部打到数据库,导致数据库连接池被打满,总请求耗时大幅增加,数据库CPU和IO飙升。 为了解决雪崩问题,文章提出了四种核心方案: 1. **过期时间随机化**:为每个缓存key的过期时间增加随机偏移量,避免集中失效。 2. **互斥锁**:当缓存失效时,只有一个线程去查询数据库,其他线程等待,防止缓存击穿。 3. **缓存预热+永不过期key**:核心数据设置永不过期,并通过后台线程定期更新缓存,避免失效。 4. **限流+降级**:通过信号量限制并发请求数,超出部分直接降级返回默认值,保护数据库。 最后,文章整合了上述方案,形成了一套完整的解决方案,优化后的请求耗时从秒级降至百毫秒级,数据库压力大幅降低。总结指出,雪崩问题的核心在于大量缓存集中失效,解决方案的重点在于避免key集中失效、防止缓存击穿、保护数据库并定期更新缓存。

Java面试-22:HashMap 的原理?

Java面试-22:HashMap 的原理?

HashMap 是一种基于数组和链表(或红黑树)的数据结构,通过 key 的 hash 值定位桶位置。数组初始长度为 16,负载因子为 0.75,当元素达到 12 个时会扩容以避免性能下降。key 的 hash 值经过扰动运算后用于定位桶位置,采用位运算而非取模以提高效率。链表长度超过 8 且数组长度大于等于 64 时,链表会转换为红黑树以优化查询性能。put 操作流程包括计算 hash、定位桶、插入元素并判断是否扩容,而 get 操作则是反向查找。HashMap 在多线程环境下可能导致环形链表,因此是非线程安全的,建议在并发场景下使用 ConcurrentHashMap。

Java面试-16:字符串常量池的作用了解吗?

Java面试-16:字符串常量池的作用了解吗?

字符串常量池是 JVM 为提升性能和减少内存消耗而专门为字符串(String 类)开辟的一块区域。其主要目的是避免字符串的重复创建,从而优化内存使用。当创建一个字符串对象时,JVM 首先在字符串常量池中查找是否已存在相同内容的字符串。如果存在,则直接返回该字符串的引用;如果不存在,则创建新字符串并将其放入常量池。这种机制确保了相同内容的字符串在内存中只存在一份,从而节省了内存空间。例如,代码中 `String aa = "ab";` 和 `String bb = "ab";` 都引用了字符串常量池中的同一个 "ab" 对象,因此 `aa == bb` 返回 `true`。

Java面试-15:String、StringBuffer、StringBuilder 的区别?

Java面试-15:String、StringBuffer、StringBuilder 的区别?

本文简要介绍了Java中的三种字符串处理类:String、StringBuilder和StringBuffer。String是不可变的字符串,线程安全,但每次修改时都会创建一个新的String对象,效率较低。StringBuilder是可变的字符串,非线程安全,但效率最高,适合在单线程环境中进行频繁的字符串操作。StringBuffer同样是可变的字符串,但线程安全,效率中等,适合在多线程环境中使用。三者在性能和线程安全性上各有特点,开发者可根据具体需求选择合适的类来处理字符串。

Java面试-14:==与equals()的区别

Java面试-14:==与equals()的区别

本文详细解析了Java中的`==`运算符和`equals()`方法的核心区别与底层原理。`==`运算符用于比较基本数据类型的数值或引用数据类型的内存地址;而`equals()`方法是`Object`类中的方法,默认比较对象的内存地址,但可以通过重写来比较对象的内容。文章通过代码示例展示了如何正确使用这两种比较方式,并强调了在重写`equals()`方法时同时重写`hashCode()`的重要性,以避免空指针异常并确保集合类的一致性。总结指出,`==`用于比较数值或地址,`equals()`则用于比较对象内容,开发中应根据需求选择合适的比较方式。

Java面试-13:接口和抽象类有什么共同点和区别?

Java面试-13:接口和抽象类有什么共同点和区别?

抽象类和接口都不能直接实例化,且都可以包含抽象方法,属于抽象层设计,用于定义规范和模板。它们的主要区别在于:抽象类支持单继承,可以定义普通变量和构造方法,目的是作为模板设计;而接口支持多实现,只能定义常量,没有构造方法,目的是规范设计。总结来说,抽象类适合代码复用,接口适合定义规范和扩展能力。

Java面试-12:面向对象的三大特性?

Java面试-12:面向对象的三大特性?

文章主要介绍了面向对象编程中的三个核心概念:封装、继承和多态。 1. **封装**:指将对象的内部状态信息隐藏在对象内部,不允许外部直接访问,但可以通过提供的方法来操作这些属性。封装可以保护对象的内部状态,同时也提供了对外交互的接口。 2. **继承**:用于在已存在的类的基础上创建新类,新类可以继承父类的属性和方法,并在此基础上扩展新的功能。继承提高了代码的重用性和可维护性,子类可以拥有父类的所有属性和方法,但无法访问父类的私有属性和方法。 3. **多态**:表示一个对象具有多种状态,具体表现为父类的引用可以指向子类的实例。多态的特点包括:对象类型和引用类型之间有继承或实现关系;方法调用的具体实现需要在运行时确定;多态不能调用子类特有的方法;如果子类重写了父类的方法,调用时会执行子类的方法。 这些概念共同构成了面向对象编程的基础,帮助开发者提高代码的模块化、重用性和可维护性。

Java面试-11:面向对象和面向过程的区别?

Java面试-11:面向对象和面向过程的区别?

面向过程编程(POP)和面向对象编程(OOP)是两种常见的编程范式,它们的主要区别在于解决问题的方式。POP通过拆分问题为一个个方法来执行,而OOP则先抽象出对象,再通过对象执行方法来解决问题。OOP相较于POP具有易维护、易复用和易扩展的优点,主要得益于其良好的结构、封装性、继承和多态特性。POP编程方式更简单直接,适合处理简单任务。需要注意的是,性能差异不仅取决于编程范式本身,还与运行机制相关,因此简单比较两者性能是一种误区。

Java面试-10:重载和重写的区别

Java面试-10:重载和重写的区别

文章主要介绍了编程中的两个重要概念:重载和重写。重载指的是同一个方法可以根据输入数据的不同,执行不同的处理逻辑。这意味着方法名相同,但参数类型或数量可以不同,从而实现不同的功能。重写则是指子类在继承父类的方法时,如果需要对相同输入数据做出不同于父类的响应,就需要覆盖父类的方法。通过重写,子类可以定制化父类的方法,以满足特定需求。这两个概念在面向对象编程中非常重要,帮助开发者实现代码的复用和灵活性。