深拷贝(clone)最佳实践 -- 考虑拷贝消耗的cpu和拷贝速度

本文探讨了Java深拷贝的性能问题,指出传统的克隆方法在高并发场景下可能导致程序卡顿。作者推荐了一款名为FastClone的工具,其在大对象深克隆上的平均耗时仅为3.4ms,比IO流和json序列化等方式快了约20倍,但不支持ConcurrentHashMap。使用FastClone可以有效避免CPU飙升和接口响应超时等问题。

深拷贝(clone)是java开发过程中经常遇到问题,有人用io流、有人用json序列化、有人循环拷贝属性等等,网上文章都能实现功能。
问题:
clone是计算操作,消耗cpu如果速度慢(高并发场景几十ms也是非常慢的)会导致程序卡顿,qps降低。网上解决方案都没有提到性能问题,我实际使用后对性能进行了优化,最后推荐:FastClone

FastClone介绍

FastClone是一款非常好用,支持大部分场景深克隆和浅克隆的工具。被推荐的不多,被埋没了。

<dependency>
	<groupId>com.github.bruce-cloud</groupId>
	<artifactId>fastclone</artifactId>
	<version>1.0.RELEASE</version>
</dependency>

用法:

// 深克隆list
FastClone fastClone = new FastClone();
List<T> clone = fastClone.clone(src);

// 深克隆list
FastClone fastClone 
<think>我们正在讨论Linux内核网络栈中`pskb_copy``skb_copy`两个函数的区别。根据引用内容,我们可以总结如下: 1. **pskb_copy**: - 拷贝`sk_buff`结构体线性数据区(即线性部分)。 - 对于非线性数据(例如分片数据,位于`skb_shared_info`结构中),它不会进行深拷贝,而是通过引用计数共享(即增加引用计数,不复制实际数据)[^3]。 - 因此,如果原始SKB有非线性数据,那么通过`pskb_copy`得到的新SKB将共享这些非线性数据(通过指针指向相同的数据区域)。 2. **skb_copy**: - 不仅拷贝`sk_buff`结构体线性数据区,还会拷贝非线性数据(即完全深拷贝)[^3]。 - 这意味着它会为非线性数据(如分片)分配新的内存,并将数据复制过去,因此新SKB与原始SKB在数据区上完全独立。 ### 关键区别总结 | 特性 | `pskb_copy` | `skb_copy` | |---------------------|----------------------------------------|-----------------------------------------| | 拷贝内容 | `sk_buff`结构体 + 线性数据 | `sk_buff`结构体 + 线性数据 + 非线性数据 | | 非线性数据处理 | 共享(引用计数增加) | 深拷贝(独立内存) | | 内存使用 | 较少(共享数据区) | 较多(完全独立拷贝) | | 适用场景 | 不需要修改非线性数据时 | 需要完全独立的SKB副本(可能修改任何部分)| ### 适用场景 - 使用`pskb_copy`: 当你只需要修改SKB的头部(例如修改协议头)或者线性数据部分,并且不会修改非线性数据(如分片数据)时,可以选择`pskb_copy`。因为它避免了复制非线性数据的开销,效率更高。 - 使用`skb_copy`: 当你需要一份完全独立的SKB副本,并且可能会修改非线性数据(例如修改分片内容)时,必须使用`skb_copy`。因为共享的非线性数据一旦被修改,会影响到原始SKB其他共享该数据的SKB。 ### 代码示例说明 假设我们有一个SKB(可能包含非线性数据): ```c struct sk_buff *skb = ...; // 原始SKB ``` - 使用`pskb_copy`: ```c struct sk_buff *pskb = pskb_copy(skb, GFP_ATOMIC); ``` 此时,如果原始SKB有非线性数据(例如通过`skb_shinfo(skb)->frags`指向的分片数组),那么`pskb`将共享这些分片数据(即`skb_shinfo(pskb)->frags`指向相同的分片数组)。因此,如果你修改了`pskb`的非线性数据,原始`skb`也会被修改。 - 使用`skb_copy`: ```c struct sk_buff *skb_copy = skb_copy(skb, GFP_ATOMIC); ``` 此时,无论原始SKB是否有非线性数据,`skb_copy`都会复制一份完整的数据,包括非线性部分。因此,修改`skb_copy`的任何数据都不会影响原始SKB。 ### 引用说明 根据引用[3]: > 前者(pskb_copy)仅仅是将sk_buff的结构体线性数据copy过来,对于非线性数据,是引用原始的skb的数据的!而后者(skb_copy)是不仅将sk_buff线性数据拷贝,同时将非线性数据也copy了一份。 这清楚地说明了两个函数在非线性数据处理上的区别。 ### 注意事项 -使用`pskb_copy`时,由于非线性数据是共享的,因此必须确保不会修改这些共享数据(除非你确实希望原始SKB其他克隆的SKB都看到修改)。如果需要修改,应该先通过`skb_linearize`将整个SKB数据线性化(变成连续线性数据),然后再修改。 - `skb_copy`虽然更安全(完全独立),但代价是更高的内存分配复制开销,特别是当SKB包含大量非线性数据(如大文件传输的分片)时。 §§相关问题§§ 1. 在什么情况下应该优先选择`pskb_copy`而不是`skb_copy`? 2. 如何判断一个SKB是否包含非线性数据? 3. `skb_linearize`函数在什么情况下会失败? 4. 为什么网络协议栈在处理大文件传输时需要非线性SKB?</think>### `pskb_copy` vs `skb_copy` 在 Linux 内核网络栈中的区别 在 Linux 内核网络栈中,`pskb_copy` `skb_copy` 都是用于复制 `sk_buff`(SKB)结构的函数,但它们在内存管理数据复制行为上有本质区别: #### 1. **数据复制范围** | 函数 | 复制内容 | 非线性数据处理 | |------|----------|----------------| | **`pskb_copy`** | 仅复制 `sk_buff` 结构体**线性数据区** | 共享原始 SKB 的非线性数据(`skb_shared_info`)[^3] | | **`skb_copy`** | 复制完整的 SKB:结构体 + 线性数据 + **非线性数据** | 深拷贝所有分片数据(完全独立副本)[^1][^3] | #### 2. **内存使用差异** ```c // pskb_copy 伪代码逻辑 n = alloc_skb(size); // 分配新SKB结构 memcpy(n->data, orig->data); // 仅复制线性数据 n->shinfo = orig->shinfo; // 共享非线性数据区 atomic_inc(orig->dataref); // 增加引用计数 ``` ```c // skb_copy 伪代码逻辑 n = alloc_skb(full_size); // 分配完整空间 memcpy(n->data, orig->data); // 复制线性数据 copy_frag_list(n, orig); // 深拷贝所有分片数据 ``` 关键区别: - `pskb_copy` 内存开销小(仅复制线性数据) - `skb_copy` 内存开销大(完全深拷贝) #### 3. **数据共享机制** | 特性 | `pskb_copy` | `skb_copy` | |------|-------------|------------| | **非线性数据共享** | 是(引用计数+1) | 否 | | **修改安全性** | 修改非线性数据会影响原始SKB | 完全隔离 | | **适用场景** | 只读访问或仅修改线性数据 | 需要修改分片数据时 | #### 4. **典型应用场景** - **使用 `pskb_copy`**: ```c // 仅需检查数据包头部的场景 struct sk_buff *clone = pskb_copy(skb, GFP_ATOMIC); iph = ip_hdr(clone); // 安全访问IP头(线性区) tcp_hdr(clone)->ack++; // 修改TCP头 ``` 适合:协议头修改、数据包监控(不需要修改分片数据)[^3] - **使用 `skb_copy`**: ```c // 需要修改分片数据的场景 struct sk_buff *deep_copy = skb_copy(skb, GFP_KERNEL); for (i = 0; i < shinfo->nr_frags; i++) { deep_copy->frags[i].page_offset += 10; // 安全修改分片 } ``` 适合:数据包内容篡改、加密操作、分片重组[^1] #### 5. **性能影响对比 | 指标 | `pskb_copy` | `skb_copy` | |------|-------------|------------| | 内存消耗 | 低(共享分片数据) | 高(完全复制) | | CPU 开销 | 低(仅复制线性数据) | 高(复制所有分片) | | 锁竞争 | 可能(引用计数原子操作) | 无 | | 吞吐量 | 适合高速路径 | 大包处理性能差 | > **设计哲学**:Linux 网络栈通过这种分层复制机制,在数据安全性性能之间取得平衡。小包处理优先使用 `pskb_copy` 减少内存复制,大包处理需要完整性时使用 `skb_copy`[^1][^3]。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值