历数Java虚拟机垃圾回收GC收集器的缺点剖析
- 作者: 五速梦信息网
- 时间: 2026年03月21日 04:43
(env->GetPrimitiveArrayCritical(mat2, &isCopyB));
mult_SSE(A, B);
env->ReleasePrimitiveArrayCritical(mat1, A, 0);
env->ReleasePrimitiveArrayCritical(mat2, B, JNI_ABORT);
}
将GetFloatArrayElements和ReleaseFloatArrayElements换成GetPrimitiveArrayCritical和ReleasePrimitiveArrayCritical就行了。CriticalArray则是为了解决数组副本问题,它是通过在GetPrimitiveArrayCritical和ReleasePrimitiveArrayCritical中创建一个阻止GC的临界区,得以将数组的真实数据直接暴露给用户。
```csharp
JNIEXPORT void JNICALL JavaCritical_cn_hotspotvm_TestArray_mul(
jint length1, jfloat* mat1,
jint length2, jfloat* mat2)
{
mult_SSE(mat1, mat2);
}
CriticalNative是一种特殊的JNI函数,整个函数都是一个临界区(当然,也包括跳过一些非关键的安全检查),能够以牺牲JVM整体稳定性获取最大的性能。 由于最初是被设计为JRE的加密模块使用,考虑到现在的加密算法大多以块为单位,换句话说大多数情况下需要在JNI中频繁传递小规模的数组,CriticalNative被专门设计对数组的传递进行优化。 JavaCritical函数相比较之前的版本,能更进一步减少JNI调用开销,这是由于它可以跳过一些“多余”的检查,并进入一个禁止JVM进行垃圾回收的临界区,以此来获得性能上的提升。
4.2、堆外内存
许多的通信框架都会开辟一块堆外内存来提高效率,如netty等。实际上,在网络和磁盘IO过程中,如果数据是在Heap里的,最终也还是会拷贝一份到堆外,然后再进行发送。原因在于,操作系统把内存中的数据写入磁盘或网络时,要求数据所在的内存区域不能变动,但是GC会对内存进行整理,导致数据内存地址发生变化,所以只能先拷贝到堆外内存(不受GC影响),然后把这个地址发给操作系统。
源代码位置:openjdk/jdk/src/share/classes/sun/nio/ch/IOUtil.java
static int read(FileDescriptor fd,
ByteBuffer dst, long position,
NativeDispatcher nd)
throws IOException
{
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
// 如果是在堆外内存DirectBuffer时,直接读取内容并返回就可以
if (dst instanceof DirectBuffer)
return readIntoNativeBuffer(fd, dst, position, nd);
// 申请一个临时的DirectBuffer
ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
try {
// 将堆中的内容拷贝到DirectBuffer
int n = readIntoNativeBuffer(fd, bb, position, nd);
bb.flip();
if (n > 0)
dst.put(bb);
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
在Java中有个DirectByteBuffer,DirectByteBuffer在创建的时候会通过Unsafe的native方法直接在Java堆外通过malloc分配一块内存,然后通过Unsafe的native方法来操作这块内存。对于需要频繁操作的内存,并且仅仅是临时存在一会的,都建议使用堆外内存,并且做成缓冲池,不断循环利用这块内存。 举个例子如下:
try (FileChannel channel = FileChannel.open(Paths.get("/tmp/data.txt"), StandardOpenOption.READ)) {
// 直接缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (channel.read(buffer) > 0) {
buffer.flip();
// 处理数据...
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
}
调用FileChannel的open()方法会返回一个FileChannelmpl实例,这个实例的read()方法会调用IOUtil.read()方法,这个方法这是我们上面介绍的方法。
相关文章
-
理解PostgreSQL和SQL Server中的文本数据类型
理解PostgreSQL和SQL Server中的文本数据类型
- 互联网
- 2026年03月21日
-
理解PHP MVC,以及NPM、YARN命令的相关说明
理解PHP MVC,以及NPM、YARN命令的相关说明
- 互联网
- 2026年03月21日
-
李天一案另四人惹不起
李天一案另四人惹不起
- 互联网
- 2026年03月21日
-
励志演讲稿,三分钟奋斗精彩演讲稿
励志演讲稿,三分钟奋斗精彩演讲稿
- 互联网
- 2026年03月21日
-
励志作文800字高中生
励志作文800字高中生
- 互联网
- 2026年03月21日
-
利用AI编程神器Cursor 从0到1开发上线个人网站,保姆级教程!
利用AI编程神器Cursor 从0到1开发上线个人网站,保姆级教程!
- 互联网
- 2026年03月21日








