首次排查 OOM 实录
一、前言
距离上篇文章更新已经一月有余,之所以一直没更新一是工作最近比较忙,二是感觉产出不了什么对自己和他人有价值的文章。因此这段时间,主要的空闲时间在学习技术和写 GitHub,博客这边就暂时落下了。
本篇文章的落成更像是一篇笔记,而不是博客。因为在一年的工作后,首次碰上了 OOM 问题,虽然导致的原因比较简单,但也算是值得纪念的,哈哈。
二、问题复现
问题原因和 Disruptor 相关,如果不了解的同学,就把它理解成一个首尾相连的环形 Queue 就 OK 了。
2.1 代码实现
首先创建 Disruptor 存放的实体类 Entity,它有个对象叫 dataList,存放的是 EntityData 的引用:
123456789101112131415@Datapublic class Entity { private long id; private List<EntityData> dataList;}@Data@NoArgsConstructor(access = AccessLevel.PRIVATE)@AllArgsCo ...
高性能 Disruptor——消除伪共享
一、CPU Cache
存储设备往往是速度越快价格越昂贵,速度越快价格越低廉。在计算机中,CPU 的速度远高于主存的速度,而主存的速度又远高于磁盘的速度。为了解决不同存储部件的速度不对等问题,让高速设备充分发挥性能,引入了多级缓存机制。
为了解决内存和 CPU 的速度不匹配问题,相继引入了 L1 Cache、L2 Cache、L3 Cache,数字越小,容量越小,速度越快,位置越接近 CPU。
现在的 CPU 都是由多个处理器,每个处理器由多个核心构成。 一个处理器对应一个物理插槽,不同的处理器间通过 QPI 总线相连。一个处理器间的多核共享 L3 Cache。一个核包含寄存器、L1 Cache、L2 Cache,下图是Intel Sandy Bridge CPU架构:
二、缓存行与伪共享
缓存中的数据并不是独立的进行存储的,它的最小存储单位是缓存行,缓存行的大小是2的整数幂个字节,最常见的缓存行大小是 64 字节。CPU 为了执行的高效,会在读取某个对象时,从内存上加载 64 的整数倍的长度,来补齐缓存行。
以 Java 的 long 类型为例,它是 8 个字节,假设我们存在 ...
不可不说的 Java “锁”事
一、前言
Java 提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自 JDK 8和 Netty 3.10.6)和使用场景进行举例,为读者介绍主流锁的知识点,以及不同的锁的适用场景。
Java 中往往是按照是否含有某一特性来定义锁,我们通过特性将锁进行分组归类,再使用对比的方式进行介绍,帮助大家更快捷的理解相关知识。下面给出本文内容的总体分类目录:
1.1 乐观锁 VS 悲观锁
乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度。在 Java 和数据库中都有此概念对应的实际应用。
先说概念,对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java 中,synchronized 关键字和 Lock 的实现类都是悲观锁。
而乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据 ...
Java 并发编程——线程池的异常处理机制
一、前言
1.1 文章起因
这篇文章的起因来源于一个 BUG,这个 BUG 和上篇文章《Java SynchronizedSet 线程不安全之坑》 有点关系。简单来说,就是在线程池中执行任务,任务本身未做异常处理,导致出现异常后任务停止。
出错的原因来自对 Collections.synchronizedSet(new HashSet<>()) 的线程不安全访问,抛出了 ConcurrentModificationException。
问题的关键是在事后查询线上日志时并没有发现相关异常记录,导致问题的排查变得困难。所幸最后找到了问题,同时也发现了默认情况下线程中的异常是不会被记录到日志中的,也算是踩了个坑吧,这就是这篇文章的由来。
1.2 问题复现
写个简单的 Case 复现一下,首先日志框架这里使用 log4j2:
12345<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifact ...
Java SynchronizedSet 线程不安全之坑
一、前言
一般而言,想要构造出线程安全的 Set,我们会使用 Collections.synchronizedSet 方法,如下所示。
1Set<User> set = Collections.synchronizedSet(new HashSet<>());
但这并不意味着,你可以安全的使用该集合的任何方法,如果没有仔细的了解过其实现的话,一不小心就会踩进坑中。最近我在使用该集合的 stream 方法时发现了线程不安全问题,都是血的教训啊,下面写个Case 来复现下吧。
二、问题引出
2.1 辅助类
本 Case 牵扯到的所有辅助类如下:
12345678910111213141516171819202122public class ThreadPoolUtils { private static final long KEEP_ALIVE_TIME = 60L; private static Logger log = LogManager.getLogger(ThreadPoolUtils.class); public st ...
Navicat Premium 15 安装与激活
偶然发现 Navicat 已经更新到了15 了,今天就来把 Navicat 升级一下。Navicat15 的激活和 Navicat12 一致,可以参考《Navicat Premium 12.0.27 安装与激活》。
首先前往官网自行下载 Navicat15 的安装包。然后下载激活工具:
Download For CSDN
Download For BaiduPan[6gwe]
以下是重点,请务必仔细阅读!!
Step0:
运行官网下载好的 Navicat15 安装包,常规安装即可。
Step1:
安装完成后,运行激活工具:Navicat Keygen Patch v5.6.0 DFoX.exe:
1)Patch勾选Backup、Host和Navicat v15,然后点击Patch按钮:
找到Navicat Premium 15安装路径下的navicat.exe,选中并点击打开:
然后弹出 Info 对话框,内容为 Cracked 就 OK 了:
Step2:
License, Product and Language 确保 License 为Enterprise,Produc ...
Parallel Stream 的错误实践
一、前言
Java8 Stream 流的出现,极大的简化了业务需求中对集合数据的加工处理操作。虽然好用,但是一旦使用不当,也会带来意想不到的结果,本文记录使用 Parallel Stream 的错误实践。
1234List<Object> sourceList = ...;List<Object> list = new ArrayList();sourceList.stream.map(...).foreach(list::add);
伪代码如上所示,对 sourceList 进行源数据加工,加工完毕后 add 进结果 list 中。运行过程中,发现其中存在 null 元素。
二、实验
写一个简单 Case 测试下,如下所示:
1234567public class StreamTest { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); IntStream.range ...
详解 Spring 定时任务的调度方式
在 Spring 中,我们可以使用 @Scheduled 方便的进行定时任务的执行,其支持以下三种调度方式:Cron、FixedDelay、FixedRate。下面分别介绍在标准模式下和异步模式下这三种调度方式的不同。
一、标准模式
1.1 示例准备
创建一个 SpringBoot 初始程序,依赖包只需要引入 spring-boot-starter-web 即可:
123456<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies>
创建类 SchedulerConfig,实现 SchedulingConfigurer 接口,作用是自定义 @Scheduled 执行的线程池配置信息。
1234567891011@Configurati ...
2019 年终总结
自从进入大学开始,总觉时间开启了快进模式,一年一年不知不觉就过去了,转瞬又是新的一年的开始。
2020 是一个特殊的年份,在不少电影中它是未来的代名词,在国家的红头文件中它是消灭贫困人口的 DeadLine。哈哈,庆幸自己还没有被消灭。
言归正传,2019 年对我来说是一个特殊的年份,因此 2020 年的第一篇博文就来总结下 2019 年吧。
一、学生
身处高考地狱难度的江苏省,机缘巧合,考入本科,走上了学计算机的道路。大学四年,让我发现时间原来可以过得这么快,总觉什么事都做了点,但又觉有许多事没做。不知给个重来的机会,能不能更好把握。
2018 年,距离本科毕业还有一年,我没有选择考研,意味着即将结束十几年的学生生涯,步入社会。迎面而来的就是秋招,最终在南京和北京的 Offer 中选择了北京。至于原因也是很简单,一是南京没啥大的互联网企业,北上广深互联网氛围更好,对职业发展有帮助;二是呆了十几年的南京,趁着年轻也想出来闯闯,哈哈。
2019 年,大学毕业,学生生涯最终画上句号。
二、工作
过完春节,来到北京正式开始实习。不知道是不是商业项目都是业务比技术复杂,一开始可以说是最痛苦 ...
gRPC 在 Java 中的入门实例
一、前言
经过前面三篇 相关文章的学习,相信大家已经对 Protobuf 有所掌握。前文说过, ProtoBuf 很适合做数据存储或 RPC 数据交换格式。可以用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
本节将介绍在 Java 中如何使用 gRPC 和 Protouf。gRpc 也是 Google 推出的 RPC 框架,由于师出同门,Protobuf 和 gRPC 可以非常容易的结合在一起,甚至于使用 Protobuf Maven 插件就可以自动生成 gRPC 代码。
如果你还没有看过前序文章,点击这里查看:
Protobuf 学习手册——语法篇
Protobuf 学习手册——编码篇
Protobuf 在 Java 中的入门实例
二、gRPC 基础介绍
2.1 gRPC 为什么使用 Protobuf
接口定义语言(Interface Definition Language -> IDL)可读性好
支持多种语言的代码生成
使用二进制格式表示数据
size 更小,相较于文本格式(如 JSON、XML)传输更快,序列化效率更高
在 ...