lz4_compress_default不检查目标缓冲区大小,易致内存越界崩溃;必须先用lz4_compressbound计算最大所需空间,再分配缓冲区,并用lz4_compress_fast等带边界检查的函数,返回0时需降级处理而非强行memcpy。

直接用 LZ4_compress_default 传指针会崩溃,不是快不快的问题
很多人以为“用指针 = 高性能”,于是把 uint8_t* 输入和输出缓冲区直接喂给 LZ4_compress_default,结果运行时偶尔 SIGSEGV。这不是偶发 bug,而是必然风险:该函数**完全不检查目标缓冲区大小**,只管往里写,一旦原始数据压缩后膨胀(极罕见但可能)、或你误估了空间,就覆盖相邻内存。真实项目中,这种越界常表现为:解压出错、结构体字段被篡改、后续 malloc 失败——但崩溃点远在调用栈下游,极难定位。
- 永远别把裸指针 + 硬编码长度传给
LZ4_compress_default - 必须先用
LZ4_compressBound(src_size)算出「最坏情况所需最大字节数」,再按此值分配目标缓冲区 - 实际压缩必须用
LZ4_compress_fast或LZ4_compress_destSize,它们会在写满时返回 0,而不是硬写越界 - 若返回 0,说明当前缓冲区不够——此时应 fallback 到未压缩存储,或重分配更大缓冲区重试(注意:不能忽略返回值强行 memcpy)
LZ4_compress_fast 的 acceleration 参数怎么设才真快
默认 acceleration=1 实际很慢,尤其对小块日志(
- acceleration=1:最全搜索,压缩率最高,但速度仅比 zlib 快 2x 左右
- acceleration=4:推荐起点,速度提升 1.7x,压缩率仅降约 3%~5%
- acceleration=16:适合纯吞吐场景(如网络包批量压缩),速度再提 30%,但小数据压缩率明显下降
- 超过 32 没意义,内部会截断为 32
注意:acceleration 不影响解压,解压永远是固定算法,无需配对设置。
用指针管理 LZ4 流状态时,LZ4_stream_t* 必须手动 free
如果你用 LZ4_createStream() 创建流对象来复用哈希表(比如连续压缩多个日志分片),那这个 LZ4_stream_t* 是堆上分配的,C 接口不会自动回收。C++ 里常见错误是:
- 局部变量声明
LZ4_stream_t* stream = LZ4_createStream();,函数返回前没调用LZ4_freeStream(stream)→ 内存泄漏 - 用 RAII 封装时,析构函数里调用
LZ4_freeStream,但忘了判空(LZ4_createStream可能返回 NULL)→ 解引用空指针 - 多线程共用一个
LZ4_stream_t*→ 数据竞争,压缩结果错乱(LZ4 流对象非线程安全)
正确做法:每个压缩线程独占一个 LZ4_stream_t*,用 std::unique_ptr + 自定义 deleter 管理,deleter 中必须判空。
压缩后 memcpy 到目标指针前,必须检查返回值是否为 0
这是最容易被忽略的一步。很多代码写成:
int compressed_size = LZ4_compress_fast(src, dst, src_size, dst_capacity, acceleration); memcpy(dst_final, dst, compressed_size); // ❌ 危险!compressed_size 可能是 0
而正确逻辑是:
- 如果
compressed_size == 0,说明目标缓冲区dst不够大,不能 memcpy,应立即 fallback - 如果
compressed_size > 0,它才是真实压缩后字节数,可安全 memcpy - 不要用
dst_capacity替代compressed_size—— 后者才是有效数据边界
真实系统里,fallback 策略往往比压缩本身更重要:要么存原始数据,要么触发异步扩容重试,但绝不能让 0 字节 memcpy 破坏后续内存布局。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











