固定对象?:
让我们先了解一下 Pinning Object.基本上,Pinning 是我们临时在堆上标记一个对象,这样垃圾回收器就不会尝试移动对象,直到我们删除标签。通常,如果对象正在升级(即从年轻空间移动到旧空间)或作为压缩(碎片整理)的一部分,则可能会从一个地址移动到另一个地址。但是,如果对象被固定,则 GC 不会尝试移动它,直到它被取消固定。
那么,我们为什么要固定一个对象呢?
固定对于简单的性能优化非常重要。在 I/O 操作期间固定缓冲区(字节数组)允许我们将其地址直接传递给操作系统。由于缓冲区已固定,因此我们不必担心垃圾回收器会在 I/O 操作完成之前尝试将其移动到其他地址。
如果我们无法固定缓冲区,则需要分配额外的本机(堆外)内存以传递给操作系统的本机 I/O 调用,并在堆上和堆外缓冲区之间复制数据。
因此,通过将缓冲区固定到堆上的常量地址,我们避免了既需要执行冗余本机内存分配又需要复制。
何时可能发生固定对象溢出?
此情况可能发生在 JNI 调用期间或 I/O 调用中的异常处理错误期间。要找出真实的事实,我们必须分析线程转储以找出有多少线程被阻塞,即。卡住了。
故障 排除:
那么,当您的线程似乎卡在对readBytesPinned或d writeBytesPinned的调用中时,您应该怎么做?这完全取决于应用程序尝试从何处读取数据或将数据写入位置。
让我们看一个真实的例子,一个线程卡住做阻塞读取:
"ExecuteThread: '2' for queue: 'weblogic.kernel.Default'" id=20 idx=0x2e tid=16946 prio=5 alive, in native, daemon
at jrockit/net/SocketNativeIO.readBytesPinned(I[BIII)I(Native Method)
at jrockit/net/SocketNativeIO.socketRead(Ljava/io/FileDescriptor;[BIII)I(Unknown Source)[inlined]
at java/net/SocketInputStream.socketRead0(Ljava/io/FileDescriptor;[BIII)I(Unknown Source)[inlined]
at java/net/SocketInputStream.read([BII)I(SocketInputStream.java:113)[optimized]
at oracle/net/ns/Packet.receive()V(Unknown Source)[inlined]
at oracle/net/ns/DataPacket.receive()V(Unknown Source)[optimized]
at oracle/net/ns/NetInputStream.getNextPacket()V(Unknown Source)[optimized]
at oracle/net/ns/NetInputStream.read([BII)I(Unknown Source)[inlined]
at oracle/net/ns/NetInputStream.read([B)I(Unknown Source)[inlined]
at oracle/net/ns/NetInputStream.read()I(Unknown Source)[optimized]
at oracle/jdbc/driver/T4CMAREngine.unmarshalUB1()S(T4CMAREngine.java:1099)[optimized]
<rest of stack omited>
在上面的例子中,你可以从堆栈跟踪中看出 JDBC(数据库)驱动程序正在从网络套接字执行阻塞读取。因此,典型的下一步是查看是否存在预期数据可能延迟(甚至根本没有到达)的原因。例如,我们正在与之交谈的数据库服务器可能挂起,可能存在延迟(甚至丢弃)数据库响应的网络问题,或者可能存在某种协议不匹配,双方都认为轮到对方说话了。分析双方的日志文件可能会提供有关所发生情况的线索。如果问题是可重现的,那么收集网络跟踪并使用WireShark等工具对其进行分析也可能很有用。
解决 方案:
找到正确的理由后,您可以编写适当的异常处理,当然,当我们完成它时,请关闭FileInputStream,以避免不必要的溢出。
参考资料:thread_stuck_at_readbytespinned_writebytespinned