我知道我可以简单地从start
迭代到end
并清除那些单元格,但我想知道是否有可能以更快的方式(可能使用JNI编辑System.arrayCopy
)?
答案 0 :(得分:17)
如果我做对了,你需要使一个数组或一个包含对象引用的数组的子范围无效,以使它们符合GC的条件。而且你有一个常规的Java数组,它可以在堆上存储数据。
回答你的问题,System.arrayCopy
是取消数组子范围的最快方法。然而,它比Arrays.fill
在内存方面更糟糕,因为在最坏的情况下,你必须分配两倍的内存来保存引用,你可以复制一个空数组。虽然如果你需要完全使数组为空,那么更快就是创建一个新的空数组(例如new Object[desiredLength]
)并用它取代你想要的数组。
Unsafe
,DirectByteBuffer
,DirectLongBuffer
实现在天真的直接实现中没有提供任何性能提升(即,如果您只是将Array
替换为DirectByteBuffer
Unsafe
或System.arrayCopy
)。它们比批量Array
慢。由于这些实现与Java unsafe.setMemory
无关,因此无论如何它们都超出了您的问题范围。
这是我的JMH基准(可用的完整基准代码via gist)代码段,包括ByteBuffer.put(long[] src, int srcOffset, int longCount)
个案例,根据@apangin评论;并根据@ jan-chaefer包括Arrays.fill
;以及@ scott-carey的等效Arrays.fill
循环来检查@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void arrayFill() {
Arrays.fill(objectHolderForFill, null);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void arrayFillManualLoop() {
for (int i = 0, len = objectHolderForFill.length; i < len; i++) {
objectHolderForLoop[i] = null;
}
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void arrayCopy() {
System.arraycopy(nullsArray, 0, objectHolderForArrayCopy, 0,
objectHolderForArrayCopy.length);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void directByteBufferManualLoop() {
while (referenceHolderByteBuffer.hasRemaining()) {
referenceHolderByteBuffer.putLong(0);
}
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void directByteBufferBatch() {
referenceHolderByteBuffer.put(nullBytes, 0, nullBytes.length);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void directLongBufferManualLoop() {
while (referenceHolderLongBuffer.hasRemaining()) {
referenceHolderLongBuffer.put(0L);
}
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void directLongBufferBatch() {
referenceHolderLongBuffer.put(nullLongs, 0, nullLongs.length);
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void unsafeArrayManualLoop() {
long addr = referenceHolderUnsafe;
long pos = 0;
for (int i = 0; i < size; i++) {
unsafe.putLong(addr + pos, 0L);
pos += 1 << 3;
}
}
@Benchmark
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public void unsafeArraySetMemory() {
unsafe.setMemory(referenceHolderUnsafe, size*8, (byte) 0);
}
是否可能是JDK 8中的内在函数。
100 elements
Benchmark Mode Cnt Score Error Units
ArrayNullFillBench.arrayCopy sample 5234029 39,518 ± 0,991 ns/op
ArrayNullFillBench.directByteBufferBatch sample 6271334 43,646 ± 1,523 ns/op
ArrayNullFillBench.directLongBufferBatch sample 4615974 45,252 ± 2,352 ns/op
ArrayNullFillBench.arrayFill sample 4745406 76,997 ± 3,547 ns/op
ArrayNullFillBench.arrayFillManualLoop sample 5549216 78,677 ± 13,013 ns/op
ArrayNullFillBench.unsafeArrayManualLoop sample 5980381 78,811 ± 2,870 ns/op
ArrayNullFillBench.unsafeArraySetMemory sample 5985884 85,062 ± 2,096 ns/op
ArrayNullFillBench.directLongBufferManualLoop sample 4697023 116,242 ± 2,579 ns/op <-- wow
ArrayNullFillBench.directByteBufferManualLoop sample 7504629 208,440 ± 10,651 ns/op <-- wow
I skipped all** the loop implementations from further tests
** - except arrayFill and arrayFillManualLoop for scale
1000 elements
Benchmark Mode Cnt Score Error Units
ArrayNullFillBench.arrayCopy sample 6780681 184,516 ± 14,036 ns/op
ArrayNullFillBench.directLongBufferBatch sample 4018778 293,325 ± 4,074 ns/op
ArrayNullFillBench.directByteBufferBatch sample 4063969 313,171 ± 4,861 ns/op
ArrayNullFillBench.arrayFillManualLoop sample 6270397 543,801 ± 20,325 ns/op
ArrayNullFillBench.arrayFill sample 6590416 548,250 ± 13,475 ns/op
10000 elements
Benchmark Mode Cnt Score Error Units
ArrayNullFillBench.arrayCopy sample 2551851 2024,543 ± 12,533 ns/op
ArrayNullFillBench.directLongBufferBatch sample 2958517 4469,210 ± 10,376 ns/op
ArrayNullFillBench.directByteBufferBatch sample 2892258 4526,945 ± 33,443 ns/op
ArrayNullFillBench.arrayFill sample 2578580 5532,063 ± 20,705 ns/op
ArrayNullFillBench.arrayFillManualLoop sample 2562569 5550,195 ± 40,666 ns/op
这是我得到的(Java 1.8,JMH 1.13,Core i3-6100U 2.30 GHz,Win10):
ByteBuffer
P.S。
说到Unsafe
和Path path = Paths.get(new File("User_privkey.pem").getAbsolutePath());
Path certPath = Paths.get(new File("User.pem").getAbsolutePath());
try {
// Used to read User_privkey.pem file to get private key
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Files.readAllBytes(path));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(spec);
// Used to read user certificate
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate cert = factory.generateCertificate(Files.newInputStream(certPath, null));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// add it to the keystore
ks.setKeyEntry("MyPKCSEntry", privateKey, "Temp".toCharArray(), new Certificate[] { cert });
File file = new File("CERTIFICATE_CUSTOMPATH");
OutputStream out = new FileOutputStream(file);
ks.store(out, "Temp".toCharArray());
out.close();
} catch (Exception e) {
System.out.println("Exception got caught" + e.getMessage());
}
- 它们的主要好处是它们可以在堆外存储数据,并且您可以实现自己的内存释放算法,这样可以比常规GC更好地处理数据结构。所以你不需要取消它们,并且可以随心所欲地压缩内存。很可能这些努力都不值得,因为现在你可以更容易地获得性能较差且更容易出错的代码。