扩展ByteBuffer类

时间:2009-03-08 22:34:51

标签: java nio bytebuffer

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

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

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

7 个答案:

答案 0 :(得分:13)

你无法扩展ByteBuffer并感谢上帝。

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

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

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

另一种方法是使用sun.misc.Unsafe并在address()之后随意访问内存,执行任何操作。

  

我想这样做   表现原因 - getInt for   例子有大约10个方法   调用,以及不少   如果的。即使剩下所有支票,也是如此   只有方法调用内联和   大/小端检查被删除,   我创建的测试显示了它   可以快4倍左右。

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

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


编辑:

如何声明任何类并绕过Java验证程序

On Unsafe:Unsafe有2个绕过验证程序的方法,如果你有一个扩展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);

答案 1 :(得分:9)

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

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

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

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

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

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

答案 2 :(得分:2)

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

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

如果您确实认为自己是正确的,请打开bug并查看他们要说的内容。

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

答案 3 :(得分:1)

  

+50赏金以避免访问限制(tt不能   仅使用反射完成。也许   有一种方法使用sun.misc.Unsafe   等?)

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

  • sun.misc.Unsafe在安全管理人员的授权下工作,因此无法帮助
  • 像Sarnum说:
  

ByteBuffer包私有   abstract _set和_get方法,所以你   无法覆盖它。还有所有   构造函数是包私有的,所以   你不能打电话给他们。

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

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

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

答案 4 :(得分:1)

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

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


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

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

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

对Unsafe类使用反射是一个开销的开销。一旦你有了Unsafe实例,就没有开销。

答案 5 :(得分:1)

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

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

答案 6 :(得分:-1)

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

如果瓶颈确实是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可以抛出的异常,也可以让你的代码JIT更加紧凑。

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