自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(30)
  • 资源 (3)
  • 收藏
  • 关注

原创 延时任务方案对比:从 Timer 到时间轮

本文对比了延时任务的四种实现方案:Timer(已淘汰)、ScheduledThreadPoolExecutor(通用首选)、DelayQueue自研调度(灵活可控)和时间轮(高并发海量任务最优)。核心结论:普通场景用堆结构足够;任务频繁创建取消且量大时,应采用时间轮以降低开销;长任务不应放入内存定时器,需结合持久化恢复机制。

2026-10-21 00:59:33 170

原创 伪共享:4个线程各改各的变量,为什么更慢

4个线程各自独立修改不同变量,本应高效并行,实测却慢数倍。根源在于这些变量因内存布局巧合挤在同一缓存行中,触发伪共享(False Sharing)。CPU缓存以64字节缓存行为单位,任一线程修改都会导致其他线程的缓存行失效,引发频繁的MESI协议同步与缓存刷新,形成“乒乓效应”。通过在变量前后填充7个无用long或使用@Contended注解,可强制隔离缓存行,消除伪共享,性能提升数倍。本质是用空间换时间,解决硬件层面的隐性竞争。

2026-10-19 22:11:56 223

原创 线上死锁不要只靠 jstack:把死锁检测做进程序,上报与自动打断恢复

线上死锁传统依赖jstack事后分析,响应慢且现场易丢失。本文提出将死锁检测嵌入程序:利用ThreadMXBean周期性扫描,自动发现并上报完整锁信息,结合interrupt尝试恢复可中断锁的死锁环。强调仅在使用可中断锁(如lockInterruptibly)时有效,否则需重启止损。检测器低频挂载于tick线程,避免性能影响,支持回调式上报与节流,实现“发现-告警-恢复”闭环,提升系统自愈能力。

2026-10-12 00:30:28 802

原创 synchronized 和 ReentrantLock 怎么选?从 AQS 讲起

synchronized 是 JVM 内置的自动释放锁,ReentrantLock 基于 AQS 实现,提供更灵活的功能。AQS 以 volatile state 和双向队列为核心,支撑锁、信号量、闭锁等同步工具。二者在无竞争下性能相当,但 ReentrantLock 支持超时、中断、公平锁和多条件唤醒,适合复杂场景;而 synchronized 更简洁安全,推荐默认使用。选择原则:简单场景用 synchronized,需高级功能才选 ReentrantLock。

2026-10-11 23:32:54 269

原创 PriorityQueue:for-each 遍历和 poll 取出,顺序一样吗?

PriorityQueue 的迭代器不保证顺序,遍历结果取决于堆的物理存储结构,而非优先级。for-each 和 new ArrayList<>(queue) 得到的都是乱序。正确做法是通过 poll() 逐个取出以获得有序结果,或对副本手动排序。避免在遍历时修改排序字段或清空队列,以防数据丢失。核心原则:取时才排序,遍历≠有序。

2026-10-11 09:52:11 353

原创 10G 堆还有余量,为什么仍然 Full GC 到 OOM?一次 JDK8 Metaspace 耗尽复盘

一台JDK8游戏服因元空间(Metaspace)耗尽引发Full GC风暴并OOM,尽管堆内存仅用1.4G。根源在于默认256M的MaxMetaspaceSize无法承载16天迭代积累的3.3万+类(含反射生成的访问器类)。通过jstat -gccause定位到Metadata GC Threshold,确认问题非堆溢出。处理方案为扩容至512M并补全GC日志,实现可观测性与容量弹性。复盘强调:元空间独立于堆管理,需单独规划,且排查应优先看GC原因而非盲目调堆。

2026-10-11 08:09:54 170

原创 JVM 堆一切正常,进程却被 OOM Killer 杀了:一次 Netty 直接内存泄漏的完整复盘

摘要(148字): 线上战斗服因客户端恒上报0确认帧,触发全量追帧重发,单秒生成3MB+超大报文。Netty堆外直接内存持续分配,速率远超释放,10分钟内耗尽物理内存,被OOM Killer强杀。JVM堆内存稳定,无Heap Dump,根源在于未设MaxDirectMemorySize,且追帧逻辑无上限保护。修复方案:熔断机制、分批重发、监控告警三管齐下,杜绝堆外内存泄漏。

2026-10-11 00:03:28 300

原创 服务启动即崩:InetAddress.getLocalHost() 抛 UnknownHostException 排查记

服务启动即崩,根源在于 InetAddress.getLocalHost() 调用失败:该方法需先获取主机名,再解析其地址,任一步骤失败均抛 UnknownHostException。常见于自定义主机名未写入 /etc/hosts、容器环境(如 Docker/K8s hostNetwork)中主机名无法解析,或镜像裁剪过度导致解析链缺失。修复方案包括在 hosts 中添加主机名与 IP 映射(推荐 127.0.0.1 用于日志),并通过脚本自动化固化规则。注意主机名应仅含字母、数字和连字符,并避免缓存干扰。

2026-10-10 23:38:29 279

原创 提前180ms的结算:NTP时钟回拨如何搞坏你的定时任务

一次180ms的时钟回拨,竟导致活动提前结算。根因在于:业务用墙钟(System.currentTimeMillis)计算任务延迟,而调度依赖单调钟(System.nanoTime),NTP步进对时引发墙钟回拨后,任务提前触发且校验失败。本文揭示“墙钟与单调钟混用”的机制性缺陷,通过10行代码复现,并提出四层防御方案,警示所有依赖定时任务的系统必须正视时间非单调性风险。

2026-10-09 23:43:42 469

原创 Core dump 崩溃排查:JVM 宕机后,那份 core 文件怎么用 gdb 还原现场

JVM 崩溃(如 SIGSEGV)时,Java 工具链失效,唯有 core dump 可还原现场。需先通过 ulimit -c unlimited 开启生成,并配置 /proc/sys/kernel/core_pattern 指定路径与命名规则(如 /data/coredump/core-%e-%p-%t-%s),确保核心转储落地。注意 systemd 服务需在 .service 文件中设置 LimitCORE=infinity,且避免 cwd 不可写或磁盘满导致 dump 失败。生产推荐使用 system

2026-10-09 22:48:52 277

原创 TreeSet / ConcurrentSkipListSet 排行榜的两个坑:改了属性 remove 失效,同分的进不来

TreeSet/ConcurrentSkipListSet 用于排行榜时,两大坑:改属性后 remove 失效(因比较器判等导致定位错误),同分玩家进不去(因 compare == 0 被视为重复元素)。根源在于有序集合用比较器判等,而非 equals。正确解法:先 remove 再修改再 add,并用 score + id 组合比较器确保唯一性。并发容器不解决语义问题,经验可平移至 Redis ZSET。

2026-10-09 07:07:58 314

原创 JVM实战:服务器堆外内存去哪了(NMT实测)

JVM进程内存超限非泄漏,而是堆外开销所致。通过-XX:NativeMemoryTracking=summary开启NMT,可精准拆解进程内存构成:堆外包括元空间、线程栈、代码缓存、GC工作内存、直接内存等。实测13000人压测下,26G堆外总开销约4G,主要来自G1的GC内存(676M)和Netty直接内存(480M)。容量规划应按堆+堆外合计(如29~30G)预估,避免被OOM killer干掉。关键指标看committed与RSS增量,而非reserved;RSS不降多因glibc arena机制,可

2026-10-08 22:35:18 511

原创 游戏服务器内存持续上涨排查实录:GC“调优“救不了你,JProfiler 两轮定位真凶

本文记录游戏服务器压测中内存持续上涨的排查全过程。通过JProfiler两轮定位,发现两大根源:一是因单位错误导致线程自旋空转,高频创建AQS Node;二是定时配置热更逻辑频繁序列化,引发Kryo洪峰。核心方法论为:先区分“分配速率”与“内存泄漏”,再用“分配树”和“diff对比”精准定位。强调“调参救不了分配过快”,修复后GC性能显著改善,揭示了“合理分配是调优前提”的本质。

2026-10-07 20:09:31 124

原创 Redis HSCAN 的 COUNT 参数“失效“了?——Hash 编码里一个隐蔽的坑

Redis HSCAN 的 COUNT 参数看似“失效”,实则因 Hash 底层编码为 ziplist 所致。当 field 数 ≤ 512 且所有 value ≤ 64B 时,Redis 使用紧凑的 ziplist 存储,导致扫描只能一次性返回全部数据,游标直接归零。此行为非 bug,而是 COUNT 仅作提示的本意体现。正确做法是:将 COUNT 视为提示,业务按任意批量处理;对大集合应分桶建模,避免单 key 过大,确保扫描可控、性能稳定。

2026-10-07 20:05:14 382

原创 服务器被挖矿病毒入侵的排查与清理实录

一次真实的挖矿病毒应急响应记录:crontab 里出现陌生任务、pstop被篡改、Redis 端口被pnscan持续扫描。本文完整还原入侵链路、清理步骤和加固方案。⚠️。

2026-10-07 00:32:49 351

原创 记一次战斗服务器 CPU 打满 100% 且“无法恢复“的排查

这次排查最后虽然是"甩锅"给了本地库的维护方,但把问题准确定位、把证据(堆栈、IO 数据、core 分析、耗时数据)整理清楚交出去,本身就是服务端开发的核心能力——否则两边只能互相猜。如果你也遇到过"看起来是 CPU 计算密集、最后根因却在别处"的案例,欢迎在评论区聊聊。标签建议:Java、性能优化、线上问题排查、arthas、Linux、游戏服务端。

2026-10-07 00:00:25 364

原创 本地缓存选型:Guava Cache、Caffeine 与 Cache2k

本文对比Guava Cache、Caffeine与Cache2k在游戏服本地缓存中的应用,通过玩家数据缓存实战案例,解析三者在淘汰算法、异步加载、性能及生态上的差异。结论:新项目首选Caffeine,性能优、支持异步加载且社区丰富;老项目可暂用Guava Cache;Cache2k适合对吞吐有极致要求的特殊场景。关键设计建议:区分refresh与expire、合理设置容量、结合过期与主动移除保障数据一致性。

2023-04-17 23:14:29 687

原创 数据库连接池方案

数据库连接池

2023-04-16 10:47:56 516 1

原创 idea中编译内存不足的解决方法

idea在build时内存不足

2023-04-16 09:31:21 5600

原创 一次FullGC问题的定位

问题背景一次在查看服务器的gc状态的时候发现,fgc的次数过高(200多次)且远大于ygc(3次),在线上环境和测试环境都有同样的现象,长时间的观察发现ygc的次数基本不变,fgc隔段时间后就会增加一次(ygc的平均时间:27ms, fgc的平均时间:58.28ms, 因为内存几乎没什么数据,fgc的时间不是很特别长)问题分析查看服务器启动期间在线人数和内存使用情况,可以看到在线人数很少,且内存的占用很低,排除内存泄露导致的JVM内存占用过高而触发fgc的原因通过jstat -gccause

2020-11-21 14:40:58 1110

原创 Powermock使用笔记

powermock使用mock与spy 的区别语法的区别:Test ms = PowerMockito.mock(Test.class);Test ms = PowerMockito.spy(new Test());Mock对象的区别:mock方法生成的对象为一个空对象,即里面所有的方法均被mock了,所以在调用该对象里面的方法时候,均不会执行该方法,仅会返回方法返回值的零值。spy方法生成的对象是一个真实的对象,即调用该对象里面的方法时,会实际执行方法中的内容。使用场景的区别:mo

2020-11-02 00:14:18 2067

原创 从源码看logback配置文件的加载顺序

从源码看logback配置文件的加载顺序配置文件加载类// 该类实现配置的初始化ch.qos.logback.classic.util.ContextInitializer配置文件public class ContextInitializer { final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy"; final public static String AUTOCONFIG_FILE = "l

2020-08-07 21:54:16 1037

原创 如何使用maven打一个直接可运行的包

如何使用maven打一个直接可运行的包以普通java项目为例,一般会遇到两个问题:1.找不到主类 2.找不到相关依赖的类找不到主类执行jar包查看jar包中的MANIFEST.MF文件,发现没要定义主类解决方式:在pom文件添加主属性信息<build> <plugins> <!--这个主要用来将主清单属性打入到jar包中,保证打包后的jar能够直接使用java -jar xxx.jar的方式直接运行-->

2020-07-04 16:44:44 662

原创 Http服务器-Netty实现

Netty实现http服务器代码实现server实现Handler实现测试Get请求POST请求解析请求中的参数GET请求POST请求代码实现server实现public class HttpServer2 { private int port; public HttpServer2(int port) { this.port = port; } public void start(){ EventLoopGroup boss =

2020-06-25 12:11:08 563

原创 idea中部分字符不显示的解决方案

问题idea编辑器中部分字体不显示,如下图:解决方式方式1:换一种字体方式2:调整字体大小对于字体敏感的可以尝试调整字体大小

2020-06-20 22:28:27 2154 1

原创 vim中多条件查询

与查找同时包含a和b的行,且无先后顺序 ?.*a\&.*b 查找同时包含a和b的行,且有先后顺序?a\&.*b或查找包含a或者b的行?a\|b

2020-06-14 08:57:05 9217

原创 DCL的理解

DCL实现代码三个问题为什么外层要判空外层加判空的目的是为了避免每次获取实例的时候都需要获取锁和释放锁,这样会带来很大的性能消耗,外层判空可以在已经初始化完成后,直接返回实例对象。为什么要内层判空内层判空是为了保证对象的单例,因为在多线程情况下,如果没有内层判空的话,那么多个线程可能在竞争锁之前都已经通过了外层判空逻辑,那么在这种情况下,会出现多个实例对象。所以加上内层判空,那么另一个线程进来后,再次判空的时候对象已经被之前释放锁的线程初始化完成,那么自然不会进入new对象的逻辑中,从

2020-05-19 23:43:35 1722

原创 终端光标快速移动快捷键分享

光标移动快捷键Ctrl + a 移动到行首Ctrl + e 移动到行尾Ctrl + f 右移1个字符Ctrl + b 左移一个字符Ctrl + t 交换光标前两个字符Ctrl + <- 左移一个单词Ctrl + -> 右移一个单词Ctrl + u 删除到行首Ctrl + k 删除到行尾Ctrl + w 删除前一个单词Ctrl + y 粘贴或者恢复上次的删除Ctrl + r 搜索History中的命令Ctrl + z : 后台运行,fg恢复...

2020-05-16 23:15:56 1590

原创 使用DateTimeFormatter遇到的一个坑及解决方式

背景描述

2020-05-16 22:50:23 6267

原创 Prometheus学习笔记

Prometheus简介是什么?特点易于管理监控服务内部运行状态强大的数据模型强大的查询语言PromQL高效可扩展、易于集成、支持丰富可视化功能原理搭建Prometheus Server运行Exporter服务欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居...

2019-11-10 00:52:53 1441

jprofiler-v9.1.1.zip

jprofiler9.1.1版本,用于分析java服务器的性能分析,能够分析cpu,内存线程等等其他信息,方便定位服务器的一些问题

2020-04-13

mat.app.zip

mac版的java内存分析工具--map,可以用来分析java的的一些内存数据,常常与jmap工具一起使用

2020-04-13

Snipaste-2.2.5-Beta.dmg.zip

mac版的截图工具,能够将截图后的图形悬浮在桌面,方便开发人员进行查看而无需进行切换。支持快捷键的使用

2020-04-13

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除