我在JNI調用中收到了導致Java端出現問題的奇怪字符串損壞。每隔一段時間,我都會在傳遞的數組中獲取一個損壞的字符串,該字符串有時具有原始未損壞字符串的現有部分。最初的C++代碼應該將數組的第一個索引設置爲地址。第二個版本使用直接緩衝區,因爲我試圖解決這個問題。模擬器在與應用程序線程分開的線程中運行,應用程序線程發佈要執行的事件。JNI字符串損壞
我之前也想過,因爲我預先分配了緩衝區,所以如果多個線程訪問套接字並導致損壞,那麼它可能會被多次使用,所以我將它轉換爲分配的Mina IoBuffer從一個池中提供ByteBuffer支持。但是,它似乎沒有任何區別。
remoteaddress[0]: 10.1.1.2:49153
remoteaddress[0]: 10.1.4.2:49153
remoteaddress[0]: 10.1.6.2:49153
remoteaddress[0]: 10.1.2.2:49153
remoteaddress[0]: 10.1.9.2:49153
remoteaddress[0]: {garbage here}
java.lang.NullPointerException
at kokuks.KKSAddress.<init>(KKSAddress.java:139)
at kokuks.KKSAddress.createAddress(KKSAddress.java:48)
at kokuks.KKSSocket._recvFrom(KKSSocket.java:963)
at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:144)
at kokuks.scheduler.RecvOperation$1.execute(RecvOperation.java:1)
at kokuks.KKSEvent.run(KKSEvent.java:58)
at kokuks.KokuKS.handleJNIEventExpiry(KokuKS.java:872)
at kokuks.KokuKS.handleJNIEventExpiry_fjni(KokuKS.java:880)
at kokuks.KokuKS.runSimulator_jni(Native Method)
at kokuks.KokuKS$1.run(KokuKS.java:773)
at java.lang.Thread.run(Thread.java:717)
remoteaddress[0]: 10.1.7.2:49153
空指針異常來自嘗試使用損壞的字符串。在C++中,地址正常打印到標準輸出,但這樣做會降低錯誤率,從我所能看到的情況來看。
的C++代碼:
/*
* Class: kokuks_KKSSocket
* Method: recvFrom2_jni
* Signature: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;IIJ)I
*/
JNIEXPORT jint JNICALL Java_kokuks_KKSSocket_recvFrom2_1jni
(JNIEnv *env, jobject obj, jstring sockpath, jobject addrbuf, jobject buf, jint position, jint limit, jlong flags) {
const char* cstr = env->GetStringUTFChars(sockpath, NULL);
std::string spath = std::string(cstr);
env->ReleaseStringUTFChars(sockpath, cstr); // release me!
if (KKS_DEBUG) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << std::endl;
}
ns3::Ptr<ns3::Object> sockobj = refmap[spath];
ns3::Ptr<ns3::Socket> socket = ns3::DynamicCast<ns3::Socket>(sockobj);
if (!socket) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " socket not found for path!!" << std::endl;
return -1; // not found
}
if (!addrbuf) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " sender address directbuffer address is null!" << std::endl;
return -1;
}
uint8_t* bufaddr = (uint8_t*)env->GetDirectBufferAddress(buf);
long bufcap = env->GetDirectBufferCapacity(buf);
uint8_t* realbufaddr = bufaddr + position;
uint32_t remaining = limit - position;
uint8_t* addrbufaddr = (uint8_t*)env->GetDirectBufferAddress(addrbuf);
long addrbufcap = env->GetDirectBufferCapacity(buf);
if (KKS_DEBUG) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " bufaddr: " << bufaddr << ", cap: " << bufcap << std::endl;
}
ns3::Address aaddr;
uint32_t mflags = flags;
int ret = socket->RecvFrom(realbufaddr, remaining, mflags, aaddr);
if (ret > 0) {
if (KKS_DEBUG) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << aaddr << std::endl;
}
ns3::InetSocketAddress insa = ns3::InetSocketAddress::ConvertFrom(aaddr);
std::stringstream ss;
insa.GetIpv4().Print(ss);
ss << ":" << insa.GetPort() << std::ends;
if (KKS_DEBUG) {
std::cout << "[kks-c~" << spath << "] " << __PRETTY_FUNCTION__ << " addr: " << ss.str() << std::endl;
}
const char *cstr = ss.str().c_str();
char *dst = (char*)addrbufaddr;
size_t len = strlen(cstr);
strncpy(dst, cstr, len + 1);
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
}
jint jret = ret;
return jret;
}
/*
* Class: kokuks_KKSNode
* Method: node_getID_jni
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_kokuks_KKSNode_node_1getID_1jni
(JNIEnv *env, jobject obj, jstring path) {
const char* cstr = env->GetStringUTFChars(path, NULL);
std::string spath = std::string(cstr);
env->ReleaseStringUTFChars(path, cstr); // release me!
if (KKS_DEBUG) {
std::cout << "[kks-c~" << spath << "?] " << __PRETTY_FUNCTION__ << std::endl;
}
ns3::Ptr<ns3::Object> nodeobj = refmap[spath];
ns3::Ptr<ns3::Node> node = ns3::DynamicCast<ns3::Node>(nodeobj);
if (node) {
uint32_t id = node->GetId();
jint j_id = id;
return j_id;
}
return -1;
}
的Java代碼(如果它幫助):
/**
*
* @param remoteaddress
* @param bytes
* @param flags
* @return
*/
protected int _core_recvFrom(final KKSAddress[] remoteaddress, final ByteBuffer bytes, final long flags) throws IOException {
if (!kks.isRealtime() || kks.isSimulationThread()) {
return _core_recvFrom_st(remoteaddress, bytes, flags);
}
boolean usejnibb = !bytes.isDirect();
final IoBuffer iob;
final ByteBuffer mybuf;
if (usejnibb) {
if (USE_IOB) {
iob = IoBuffer.allocate(bytes.remaining(), true);
mybuf = iob.buf();
} else {
mybuf = jnibb;
}
mybuf.clear();
mybuf.limit(bytes.remaining());
} else {
mybuf = bytes;
iob = null;
}
try {
KKSEvent<Integer> kev = new KKSSocketEvent<Integer>(this) {
@Override
protected Integer execute(long timeMS) throws IOException {
return _core_recvFrom_st(remoteaddress, mybuf, flags);
}
/* (non-Javadoc)
* @see kokuks.KKSEvent#getType()
*/
public String getType() {
return "_core_recvFrom()";
}
};
try {
int ret = kks.scheduleEventRTWait(kev);
if (ret > 0 && usejnibb) {
mybuf.flip();
bytes.put(mybuf);
}
return ret;
} catch (InterruptedException e) {
throw new InterruptedIOException();
} catch (EventExecException e) {
if (e.getCause() instanceof IOException) {
throw (IOException)e.getCause();
}
throw new IOException(e.getCause());
} catch (Exception e) {
throw new IOException(e);
}
} finally {
if (usejnibb) {
if (USE_IOB) {
iob.free();
}
}
}
}
/**
* Pass an array of size 1 into remote address, and this will be set with
* the sender of the packet (hax). This emulates C++ references.
*
* @param remoteaddress
* @param buf
* @param flags
* @return
*/
protected int _core_recvFrom_st(final KKSAddress[] remoteaddress, ByteBuffer buf, long flags) throws IOException {
try {
_syncJNI();
boolean recvfrom = remoteaddress != null;
errNo = SocketErrno.ERROR_NOTERROR;
ByteBuffer mybuf = buf;
if (!buf.isDirect()) {
errNo = SocketErrno.ERROR_BUFFERNOTDIRECT;
throw new IllegalArgumentException("Buffer not direct!");
}
final IoBuffer iob;
ByteBuffer bb = null;
if (recvfrom) {
if (USE_IOB) {
iob = IoBuffer.allocate(128, true);
bb = iob.buf();
} else {
bb = addrbb;
}
bb.clear();
} else {
iob = null;
}
try {
//IoBuffer pre = IoBuffer.wrap(mybuf.duplicate());
//printMessage("sockrecv (pre) // rxavailable: " + getRxAvailable());
// use new mechanism
int ret = recvfrom ?
recvFrom2_jni(
path.toPortableString(),
bb,
mybuf,
mybuf.position(),
mybuf.limit(),
flags
) : recv_jni(
path.toPortableString(),
mybuf,
mybuf.position(),
mybuf.limit(),
flags
);
_syncJNI();
if (ret >= 0) {
rxTotal += ret;
/*
printMessage("local addr: " + LOCAL_ADDR + ", real local addr: " + getRemoteAddress().toNormalAddress());
printMessage("remote addr: " + REMOTE_ADDR + ", real remote addr: " + getApp().getNode().getIPV4Address());
if (
getType() == SOCKET_TYPE_TCP &&
getRemoteAddress().toNormalAddress().equals(LOCAL_ADDR) &&
getApp().getNode().getIPV4Address().equals(REMOTE_ADDR)
) {
mrTest_testRecvd(mybuf, ret);
}
*/
//printMessage("sockrecv // mybuf: " + mybuf + ", ret: " + ret + " rxavailable: " + getRxAvailable() + ", data: " + BufUtils.asText(mybuf, ret));
buf.position(buf.position() + ret);
if (recvfrom) {
String st;
try {
st = IoBuffer.wrap(bb).getString(CDE);
remoteaddress[0] = KKSAddress.createAddress(st);
if (remoteaddress[0] == null) {
System.out.println("warning; remote address is null!! original: " + st);
}
} catch (CharacterCodingException e) {
e.printStackTrace();
}
}
return ret;
//pre.limit(pre.position() + ret);
//printMessage("_core_recvFrom_st recvd from " + ((!recvfrom || remoteaddress[0] == null || remoteaddress == null) ? getRemoteAddress() : remoteaddress[0]) + ": " + pre.getHexDump());
}
throw new IOException("I/O exception, retval: " + ret + ", errNo: " + errNo);
} finally {
if (recvfrom) {
if (USE_IOB) {
iob.free();
}
}
}
} finally {
errNo = _getErrNo();
}
}
編輯:我還確定了分組數據是越來越損壞。此外,我的工作電腦上發生的問題比我的家用電腦多。我的工作電腦運行Windows XP,擁有4GB內存和Q6600,我的家用電腦擁有超頻Q6600,4GB內存,並且運行Windows 7 64位,儘管它採用32位Java。
我仍然得到這個問題,一年後,在完全相同的方法(或多或少)。真的很煩人。有完整的線程安全性,並且我對線程方面的知識增加了十倍。但是,我仍然遇到這個問題。我懷疑這與線程有關。所有變量都在堆棧中。 – 2011-05-16 18:52:49
我在代碼中看到了很多問題,「addrbb」是共享的,所以你希望它是一個ThreadLocal。你不檢查C代碼中的NULL結果(但應該是小代碼),通常ByteBuffer的標準方法是在C中處理ByteBuffer.address,如'void *'。另外一個注意事項:java中的ByteBuffer是至少分配了一個頁面大小(通常是4k)。 'ByteBuffer.duplicate'只是在相同的底層支持區域創建java對象,所以在你的情況下它是無用的,如果你不想節省地址空間,你可以使用ByteBuffer.slice。 –
bestsss
2011-05-17 07:08:50
IoBuffer使用ThreadLocal ByteBuffer分配池:)我認爲問題可能是直接使用Java值,而不是在不同的行上進行轉換。 long bufcap = env-> GetDirectBufferCapacity(buf); uint8_t * realbufaddr = bufaddr + position; 我在過去曾經遇到過一些問題,我嘗試過在單行上做,而且應用程序失敗(主要是返回值)。 jlong j_bufcap = env-> GetDirectBufferCapacity(buf); long bufcap = j_bufcap; uint8_t * realbufaddr = bufaddr + position; 可能已經修復它,但需要更多的測試。 – 2011-05-17 16:53:45