Java中内存分配的典型速度是多少?

我正在分析Java应用程序,并发现对象分配发生的速度比我预期的要慢得多。 我运行了一个简单的基准测试来尝试建立小对象分配的整体速度,我发现在我的机器上分配一个小对象(3个浮点数的向量)似乎需要大约200纳秒。 我正在运行(双核)2.0 GHz处理器,因此大约有400个CPU周期。 我想问一下这里的人们是否有关于Java应用程序的概述,然后才能预期这种速度。 这对我来说似乎有点残忍和不寻常。 毕竟,我认为像Java这样可以压缩堆并重定位对象的语言会让对象分配如下所示:

int obj_addr = heap_ptr; heap_ptr += some_constant_size_of_object return obj_addr; 

….这是几行assembly。 至于垃圾收集,我不会分配或丢弃足够的对象来进行游戏。 当我通过重用对象来优化我的代码时,我得到的性能大约为15纳秒/对象我需要处理而不是每个对象需要处理200 ns,因此重用对象可以极大地提高性能。 我真的很想不重用对象,因为这会使符号变得多毛(许多方法需要接受receptacle参数而不是返回值)。

所以问题是:对象分配花费这么长时间是否正常? 或者我的机器上可能出现问题,一旦修复,可能会让我在这方面有更好的表现? 小对象分配通常需要多长时间用于其他人,并且是否有典型值? 我正在使用客户端计算机,目前不使用任何编译标志。 如果您的机器上的速度更快,那么您机器的JVM版本和操作系统是什么?

我意识到个人里程在性能方面可能会有很大差异,但我只想问我上面提到的数字是否正好在他们正确的范围内。

当对象很小且没有GC成本时,创建对象非常快。

 final int batch = 1000 * 1000; Double[] doubles = new Double[batch]; long start = System.nanoTime(); for (int j = 0; j < batch; j++) doubles[j] = (double) j; long time = System.nanoTime() - start; System.out.printf("Average object allocation took %.1f ns.%n", (double) time/batch); 

-verbosegc打印

 Average object allocation took 13.0 ns. 

注意:没有发生GC。 但是增加了大小,程序需要等待在GC中复制内存。

 final int batch = 10 *1000 * 1000; 

版画

 [GC 96704K->94774K(370496K), 0.0862160 secs] [GC 191478K->187990K(467200K), 0.4135520 secs] [Full GC 187990K->187974K(618048K), 0.2339020 secs] Average object allocation took 78.6 ns. 

我怀疑你的分配相对较慢,因为你正在执行GC。 解决此问题的一种方法是增加应用程序可用的内存。 (虽然这可能会延迟成本)

如果我用-verbosegc -XX:NewSize=1g再次运行它-verbosegc -XX:NewSize=1g

 Average object allocation took 9.1 ns. 

我不知道你如何衡量分配时间。 它可能至少相当于

 intptr_t obj_addr = heap_ptr; heap_ptr += CONSTANT_SIZE; if (heap_ptr > young_region_limit) call_the_garbage_collector (); return obj_addr; 

但它比这更复杂,因为你必须填写obj_addr ; 然后,可能会发生一些JIT编译或类加载 ; 很可能,前几个单词被初始化(例如,类指针和哈希码,可能涉及一些随机数生成…),并调用对象构造函数。 他们可能需要同步等。

更重要的是,新分配的对象可能不在最近的一级缓存中,因此可能会发生一些缓存未命中。

因此,虽然我不是Java专家,但我并没有因为您的措施而感到高兴。 我相信分配新鲜对象会使代码更清晰,更易于维护,而不是尝试重用旧对象。

是。 你认为它应该做的和它实际做的之间的差异可能非常大。 池化可能很混乱,但是当分配和垃圾收集是执行时间的很大一部分时,它肯定是可以的,池化是一个巨大的胜利,性能方面。

池中的对象是您在分配过程中经常通过堆栈样本找到的对象。

这是C ++中这样的示例。 在Java中,细节是不同的,但这个想法是一样的:

 ... blah blah system stuff ... MSVCRTD! 102129f9() MSVCRTD! 1021297f() operator new() line 373 + 22 bytes operator new() line 65 + 19 bytes COpReq::Handler() line 139 + 17 bytes <----- here is the line that's doing it doit() line 346 + 12 bytes main() line 367 mainCRTStartup() line 338 + 17 bytes KERNEL32! 7c817077() V------ and that line shows what's being allocated COperation* pOp = new COperation(iNextOp++, jobid);