Java的readBytes是怎么實(shí)現(xiàn)的?
1.前言
眾所周知,Java是一門跨平臺(tái)語(yǔ)言,針對(duì)不同的操作系統(tǒng)有不同的實(shí)現(xiàn)。本文從一個(gè)非常簡(jiǎn)單的api調(diào)用來(lái)看看Java具體是怎么做的.
2.源碼分析
從FileInputStream.java中看到readBytes最后是native調(diào)用
/**
? ? * Reads a subarray as a sequence of bytes.
? ? * @param b the data to be written
? ? * @param off the start offset in the data
? ? * @param len the number of bytes that are written
? ? * @exception IOException If an I/O error has occurred.
? ? */
? ?private native int readBytes(byte b[], int off, int len) throws IOException; // native調(diào)用
? ?/**
? ? * Reads up to <code>b.length</code> bytes of data from this input
? ? * stream into an array of bytes. This method blocks until some input
? ? * is available.
? ? *
? ? * @param ? ? ?b ? the buffer into which the data is read.
? ? * @return ? ? the total number of bytes read into the buffer, or
? ? * ? ? ? ? ? ? <code>-1</code> if there is no more data because the end of
? ? * ? ? ? ? ? ? the file has been reached.
? ? * @exception ?IOException ?if an I/O error occurs.
? ? */
? ?public int read(byte b[]) throws IOException { ? ? ? ?return readBytes(b, 0, b.length);
? ?}
從jdk源碼中,我們找到FileInputStream.c(/jdk/src/share/native/java/io),此文件定義了對(duì)應(yīng)文件的native調(diào)用.
// FileInputStream.cJNIEXPORT jint JNICALLJava_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this,
? ? ? ?jbyteArray bytes, jint off, jint len) { ? ?return readBytes(env, this, bytes, off, len, fis_fd);
}
我們觀察下當(dāng)前的目錄,可以看到j(luò)ava 對(duì)典型的四種unix like的系統(tǒng)(bsd, linux, macosx, solaris), 以及windows 提供了特殊實(shí)現(xiàn)。share是公用部分。

在頭部獲取文件fd field (fd 是非負(fù)正整數(shù),用來(lái)標(biāo)識(shí)打開(kāi)文件)
// FileInputStream.cJNIEXPORT void JNICALLJava_java_io_FileInputStream_initIDs(JNIEnv *env, jclass fdClass) {
? ?fis_fd = (*env)->GetFieldID(env, fdClass, "fd", "Ljava/io/FileDescriptor;"); /* fd field,后面用來(lái)獲取 fd */}
?繼續(xù)調(diào)用readBytes
// ioutil.cjintreadBytes(JNIEnv *env, jobject this, jbyteArray bytes,
? ? ? ? ?jint off, jint len, jfieldID fid){
? ?jint nread; ? ?char stackBuf[BUF_SIZE]; ? ?char *buf = NULL;
? ?FD fd; ? ?if (IS_NULL(bytes)) {
? ? ? ?JNU_ThrowNullPointerException(env, NULL); ? ? ? ?return -1;
? ?} ? ?if (outOfBounds(env, off, len, bytes)) { /* 越界判斷 */
? ? ? ?JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL); ? ? ? ?return -1;
? ?} ? ?if (len == 0) { ? ? ? ?return 0;
? ?} else if (len > BUF_SIZE) {
? ? ? ?buf = malloc(len); /* 緩沖區(qū)不足,動(dòng)態(tài)分配內(nèi)存 */
? ? ? ?if (buf == NULL) {
? ? ? ? ? ?JNU_ThrowOutOfMemoryError(env, NULL); ? ? ? ? ? ?return 0;
? ? ? ?}
? ?} else {
? ? ? ?buf = stackBuf;
? ?}
? ?fd = GET_FD(this, fid); /* 獲取fd */
? ?if (fd == -1) {
? ? ? ?JNU_ThrowIOException(env, "Stream Closed");
? ? ? ?nread = -1;
? ?} else {
? ? ? ?nread = IO_Read(fd, buf, len); /* 執(zhí)行read,系統(tǒng)調(diào)用 */
? ? ? ?if (nread > 0) {
? ? ? ? ? ?(*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);
? ? ? ?} else if (nread == -1) {
? ? ? ? ? ?JNU_ThrowIOExceptionWithLastError(env, "Read error");
? ? ? ?} else { /* EOF */
? ? ? ? ? ?nread = -1;
? ? ? ?}
? ?} ? ?if (buf != stackBuf) { ? ? ? ?free(buf); /* 失敗釋放內(nèi)存 */
? ?} ? ?return nread;
}
我們繼續(xù)看看IO_Read的實(shí)現(xiàn),是個(gè)宏定義
#define IO_Read handleRead
handleRead有兩種實(shí)現(xiàn)
solaris實(shí)現(xiàn):
// /jdk/src/solaris/native/java/io/io_util_md.cssize_thandleRead(FD fd, void *buf, jint len){ ? ?ssize_t result;
? ?RESTARTABLE(read(fd, buf, len), result); ? ?return result;
}/*
* Retry the operation if it is interrupted
*/ } while(0)
read方法可以參考unix man page
windows實(shí)現(xiàn):
// jdk/src/windows/native/java/io/io_util_md.cJNIEXPORT
jinthandleRead(FD fd, void *buf, jint len){
? ?DWORD read = 0;
? ?BOOL result = 0;
? ?HANDLE h = (HANDLE)fd; ? ?if (h == INVALID_HANDLE_VALUE) { ? ? ? ?return -1;
? ?}
? ?result = ReadFile(h, ? ? ? ? ?/* File handle to read */
? ? ? ? ? ? ? ? ? ? ?buf, ? ? ? ?/* address to put data */
? ? ? ? ? ? ? ? ? ? ?len, ? ? ? ?/* number of bytes to read */
? ? ? ? ? ? ? ? ? ? ?&read, ? ? ?/* number of bytes read */
? ? ? ? ? ? ? ? ? ? ?NULL); ? ? ?/* no overlapped struct */
? ?if (result == 0) { ? ? ? ?int error = GetLastError(); ? ? ? ?if (error == ERROR_BROKEN_PIPE) { ? ? ? ? ? ?return 0; /* EOF */
? ? ? ?} ? ? ? ?return -1;
? ?} ? ?return (jint)read;
}
3.java異常初探
// jdk/src/share/native/common/jni_util.c/**
* Throw a Java exception by name. Similar to SignalError.
*/JNIEXPORT void JNICALLJNU_ThrowByName(JNIEnv *env, const char *name, const char *msg){
? ?jclass cls = (*env)->FindClass(env, name); ? ?if (cls != 0) /* Otherwise an exception has already been thrown */
? ? ? ?(*env)->ThrowNew(env, cls, msg); /* 調(diào)用JNI 接口*/}/* JNU_Throw common exceptions */JNIEXPORT void JNICALLJNU_ThrowNullPointerException(JNIEnv *env, const char *msg){
? ?JNU_ThrowByName(env, "java/lang/NullPointerException", msg);
}
最后是調(diào)用JNI:
// hotspot/src/share/vm/prims/jni.hjint ThrowNew(jclass clazz, const char *msg) { ? ? ? ?return functions->ThrowNew(this, clazz, msg);
? ?}
jint (JNICALL *ThrowNew)
? ? ?(JNIEnv *env, jclass clazz, const char *msg);
4.總結(jié)
很多高級(jí)語(yǔ)言,有著不同的編程范式,但是歸根到底還是(c語(yǔ)言)系統(tǒng)調(diào)用,c語(yǔ)言能夠在更低的層面做非常多的優(yōu)化。如果我們了解了這些底層的系統(tǒng)調(diào)用,就能看到問(wèn)題的本質(zhì)。
本文沒(méi)有對(duì)JNI 做深入分析,后續(xù)繼續(xù)解析。