我继承了一段代码,它大量使用 String -> byte[] 转换,反之亦然,用于一些自制的序列化代码。本质上,Java 对象知道如何将它们的组成部分转换为字符串,然后再转换为字节 []。然后,所述字节数组通过 JNI 传递到 C++ 代码中,该代码将 byte[] 重构为 C++ std::strings 并使用这些代码引导反映 Java 对象的 C++ 对象。还有更多内容,但这是这段代码如何工作的高级视图;通信在两个方向上都是这样工作的,因此 C++ -> Java 转换是我上面提到的 Java -> C++ 转换的镜像。
这段代码的一部分——将 String 实际转换为 byte[]——出乎意料地在分析器中显示为消耗大量 CPU。当然,有很多数据正在传输,但这是一个意想不到的瓶颈。
代码的基本大纲如下:
public void convertToByteArray(String convert_me, ByteArrayOutputStream stream)
{
stream.write(convert_me.getBytes());
}
该功能还有一点点,但不多。上面的函数会为每个 String/Stringified 对象调用一次,并且在将所有成分写入 ByteArrayOutputStream 之后,ByteArrayOutputStream 会转换为 byte[]。通过提取调用将上述内容分解为对分析器更友好的版本convert_me.getBytes()
表明,此函数中超过 90% 的时间都花在了 getBytes() 调用中。
有没有办法提高 getBytes() 调用的性能,或者是否有另一种可能更快的方法来实现相同的转换?
正在转换的对象数量非常大。在仅使用一小部分生产数据的分析运行中,我看到对上述转换函数的调用超过 1000 万次。
由于我们非常接近将项目发布到生产环境中,因此目前无法采用一些解决方法:
- 重写序列化接口,只在 JNI 层传递字符串对象。这是改善情况的明显(对我而言)方法,但它需要对序列化层进行重大重新设计。鉴于我们将在本周早些时候进入 UAT,现在进行这种复杂的更改为时已晚。这是我下一个版本的首要任务,所以它会完成;然而,在那之前我确实需要一个解决方法,但到目前为止代码正在运行,已经使用了多年并且大部分问题都解决了。好吧,除了表演。
- 更改 JVM(当前为 1.5)也不是一种选择。不幸的是,这是安装在客户端机器上的默认 JVM,很遗憾无法更新到 1.6(在这种情况下可能会更快,也可能不会更快)。任何在大型组织工作过的人都可能明白为什么...
- 除此之外,我们已经遇到了内存限制,因此尝试缓存至少较大的字符串及其字节数组表示,虽然是一个潜在的优雅解决方案,但可能会导致比它解决的问题更多的问题