扩展ByteBuffer类

有没有办法创建扩展ByteBuffer类的类?

ByteBuffer的一些抽象方法是包私有的,如果我创建包java.nio,则抛出安全exception。

出于性能原因,我想这样做 – 例如,getInt有大约10个方法调用,以及相当多的if。 即使剩下所有检查,并且只有方法调用被内联并且大/小端检查被删除,我创建的测试表明它可以快4倍。

你不能扩展ByteBuffer并感谢上帝。

你不能延长b / c没有受保护的c-tors。 为什么感谢上帝? 好吧,只有2个真正的子类可以确保JVM可以重新优化任何涉及ByteBuffer的代码。

最后,如果你需要扩展类的实际,编辑字节代码,只需将c-tor和public属性的protected属性添加到DirectByteBuffer(和DirectByteBufferR)。 扩展HeapBuffer没有任何意义,因为无论如何都可以访问底层数组

使用-Xbootclasspath/p并在那里添加自己的类,在你需要的包中扩展(在java.nio之外)。 这就是它的完成方式。

另一种方法是使用sun.misc.Unsafe并在address()之后直接访问内存,做任何你需要的事情。

出于性能原因,我想这样做 – 例如,getInt有大约10个方法调用,以及相当多的if。 即使剩下所有检查,并且只有方法调用被内联并且大/小端检查被删除,我创建的测试表明它可以快4倍。

现在好的部分,使用gdb并检查真正生成的机器代码,您会惊讶地发现将删除多少检查。

我无法想象为什么一个人想扩展课程。 它们的存在是为了获得良好的性能而不仅仅是OO多态执行。


编辑:

如何声明任何类并绕过Javavalidation程序

On Unsafe:Unsafe有2个绕过validation程序的方法,如果你有一个扩展ByteBuffer的类,你可以调用它们中的任何一个。 对于编译器,你需要一些带有公共访问和受保护的c-tor的ByteBuffer的黑客版本(但这非常简单)。 方法如下。 你可以自己承担风险。 声明类之后你甚至可以使用w / new关键字(前提是有一个合适的c-tor)

 public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); public native Class defineClass(String name, byte[] b, int off, int len); 

您可以通过使用reflection来忽略保护级别,但这有点大大地击败了性能目标。

你不能在java.nio包中创建一个类 – 这样做(并以任何方式分发结果)违反了Sun的Java许可证,理论上可能会让你陷入法律麻烦。

我不认为有一种方法可以做到你想要做的事情而不是原生 – 但我也怀疑你屈服于过早优化的诱惑。 假设您的测试是正确的(通常不是哪些微基准测试):您是否真的确定对ByteBuffer的访问将成为您实际应用程序中的性能瓶颈? 当你的应用程序只花费5%的时间在95%处理所提取的数据时,ByteBuffer.get()的速度是否快4倍是无关紧要的。

想要绕过所有检查(可能纯粹是理论上的)性能并不是一个好主意。 性能调优的基本规则是“首先让它正常工作,然后让它更快地工作”。

编辑:如果,如评论中所述,应用程序实际上确实花了20-40%的时间在ByteBuffer方法中并且测试是正确的,这意味着加速潜力为15-30% – 显着,但IMO不值得启动使用JNI或搞乱API源。 我会先尝试用尽所有其他选择:

  • 你在使用-server VM吗?
  • 可以修改应用程序以减少对ByteBuffer的调用,而不是试图加快它所做的那些吗?
  • 使用分析器查看呼叫的来源 – 也许有些是完全没必要的
  • 也许算法可以修改,或者你可以使用某种缓存

ByteBuffer是抽象的,是的,你可以扩展它…但我认为你想要做的是扩展实际实例化的类,你可能不会。 也可能是,实例化的特定方法会覆盖该方法,使其比ByteBuffer中的方法更有效。

我还会说你可能总是对所有需要的东西都是错的 – 也许它不适合你正在测试的东西,但可能代码是有原因的(可能在其他平台上)。

如果您确实认为自己是正确的,请打开一个错误 ,看看他们要说些什么。

如果要添加到nio包,可以在调用Java时尝试设置引导类路径。 它应该让你在rt.jar之前放入你的课程。 键入java -X以查看如何执行此操作,您需要-Xbootclasspath / p开关。

+50赏金以避免访问限制(tt无法单独使用reflection完成。也许有一种方法可以使用sun.misc.Unsafe等?)

答案是:没有办法规避Java中的所有访问限制。

  • sun.misc.Unsafe在安全管理员的授权下工作,所以它无济于事
  • 像Sarnum说:

ByteBuffer具有包私有abstract _set和_get方法,因此您无法覆盖它。 并且所有构造函数都是包私有的,因此您无法调用它们。

  • Reflection允许您绕过很多东西,但前提是安全管理器允许它。 在许多情况下,您无法控制安全管理器,它会强加给您。 如果您的代码依赖于摆弄安全管理器,那么在任何情况下都不会“可移植”或可执行,可以这么说。

问题的底线是尝试覆盖字节缓冲区不会解决问题。

除了您自己实现一个类,没有其他选择,使用您需要的方法。 使方法最终可以帮助编译器执行优化(减少为运行时多态性和内联生成代码的需要)。

获取Unsafe实例的最简单方法是通过reflection。 但是,如果您无法使用reflection,则可以创建另一个实例。 你可以通过JNI做到这一点。

我尝试使用字节代码,创建一个不调用构造函数的实例,允许您创建一个没有可访问构造函数的对象实例。 但是,这个id不起作用,因为我得到了字节码的VerifyError。 该对象必须有一个调用它的构造函数。


我所做的是有一个包含直接ByteBuffer的ParseBuffer。 我使用reflection来获取Unsafe引用和address 。 为了避免运行缓冲区的末端并终止JVM,我分配了比我需要的页面更多的页面,只要它们没有被触及,就不会为应用程序分配物理内存。 这意味着我的边界检查要少得多,只能检查关键点。

使用OpenJDK的调试版本,您可以看到Unsafe get / put方法变为单个机器代码指令。 但是,并非所有JVM都提供此function,并且可能无法在所有平台上获得相同的改进。

使用这种方法我会说你可以减少40%的时间,但是存在普通Java代码没有的风险,即你可以杀死JVM。 我使用的用例是一个对象创建免费XML解析器和使用Unsafe包含的数据处理器与使用普通直接ByteBuffer相比。 我在XML解析器中使用的一个技巧是getShort()和getInt()一次检查多个字节,而不是一次检查一个字节。

使用reflection到Unsafe类是一个你需要的开销。 一旦你有Unsafe实例,就没有开销。

Java代理可以修改ByteBuffer的字节码并更改构造函数的访问修饰符。 当然,您需要在JVM上安装代理,并且仍然需要编译获取子类进行编译。 如果您正在考虑进行此类优化,那么您必须为此做好准备!

我从来没有尝试过如此低级别的操纵。 希望在您的代理可以挂钩之前,JVM不需要ByteBuffer。

我正在回答你想要答案的问题,而不是你问过的问题。 你真正的问题是“我怎样才能让它变得更快?” 答案是“一次处理一个数组的整数,而不是单独处理。”

如果瓶颈确实是ByteBuffer.getInt()或ByteBuffer.getInt(location),那么您不需要扩展类,您可以使用预先存在的IntBuffer类来批量获取数据以进行更有效的处理。

 int totalLength = numberOfIntsInBuffer; ByteBuffer myBuffer = whateverMyBufferIsCalled; int[] block = new int[1024]; IntBuffer intBuff = myBuffer.asIntBuffer(); int partialLength = totalLength/1024; //Handle big blocks of 1024 ints at a time try{ for (int i = 0; i < partialLength; i++) { intBuff.get(block); // Do processing on ints, w00t! } partialLength = totalLength % 1024; //modulo to get remainder if (partialLength > 0) { intBuff.get(block,0,partialLength); //Do final processing on ints } } catch BufferUnderFlowException bufo { //well, dang! } 

这比一次获得一个int要快得多。 迭代int []数组,它具有set和known-good bounds,通过消除边界检查和ByteBuffer抛出的exception,也可以让你的代码JIT更加紧凑。

如果您需要进一步的性能,可以调整代码,或将自己的大小优化的byte []转换为int []转换代码。 使用它代替IntBuffer方法并部分循环展开,我能够获得一些性能提升……但是没有任何方法建议。