尝试使用将对象转换为字节数组时频繁进行 GC

Frequent GC when trying to use convert Object to byte array

我正在使用以下代码将对象转换为字节数组。

ByteArrayOutputStream baoS= new ByteArrayOutputStream();        
objectOutputStream ooS = new ObjectOutputStream(baoS);
ooS.writeObject(object);        
ooS.flush();        
ooS.close();
return baoS.toByteArray();

我们在生产环境中面临频繁的 GC 问题和缓慢。 我们有很多对象在运行时出现,上面的代码将被调用以将对象转换为字节数组。如果频繁调用,此代码是否会导致内存泄漏/更多 CPU 使用? 传递的对象是一个列表。在使用 JVisualVM 进行采样时,此方法已成为瓶颈。

如果可以的话,简单的解决方案是添加更多堆。根据请求量,这可能比支付薪水来编写不同的解决方案更便宜。这样就没有代码更改、发布验证等。

The object passed is a List. This method has come as a bottleneck when sampling done with JVisualVM.

如果列表是巨大的,那么堆上现在有 两个 巨大的东西:

  1. 列表
  2. 列表的字节数组版本

2是骗人的。 ByteArrayOutputStream 中的默认缓冲区是 32 字节。大于该值的消息将导致创建 另一个 两倍大的数组并复制数据。如果这还不够大,请创建 另一个 数组并再次复制。等等等等。工作量很大!

现在对同时传入的所有请求并行执行此操作。然后,当所有工作完成后,您需要收回所有这些巨大的东西。这可以解释 gc.

下一个简单的解决方案是预先调整缓冲区的大小。如果您知道 98% 的消息大小小于 16k,那么您可以预先声明:new ByteArrayOutputStream(16384);。现在,从 32 字节增长到 16834 的所有数组副本中的 98% 都消失了。的确,这对于实际上很小的消息来说效率很低,但也许这是一个净赢。

或者不是固定缓冲区大小,也许你可以猜测:new ByteArrayOutputStream(guessBufferSize(theList));

更难的解决方案是您可以尝试通过从堆中取出东西来缓解堆内存压力:将字节写入其他地方,比如写入磁盘上的临时文件并将文件路径发送到 jms 发送小部件。写入磁盘会使速度变慢,但这样做会减少堆抖动,从而使速度变快。