詳解Java的readBytes是怎么實現的
1.前言
眾所周知,Java是一門跨平臺語言,針對不同的操作系統有不同的實現。本文從一個非常簡單的api調用來看看Java具體是怎么做的.
2.源碼分析
從FileInputStream.java中看到readBytes最后是native調用
/** * 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調用 /** * 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),此文件定義了對應文件的native調用.
// FileInputStream.c JNIEXPORT jint JNICALL Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this, jbyteArray bytes, jint off, jint len) { return readBytes(env, this, bytes, off, len, fis_fd); }
我們觀察下當前的目錄,可以看到java 對典型的四種unix like的系統(bsd, linux, macosx, solaris), 以及windows 提供了特殊實現。share是公用部分。
在頭部獲取文件fd field (fd 是非負正整數,用來標識打開文件)
// FileInputStream.c JNIEXPORT void JNICALL Java_java_io_FileInputStream_initIDs(JNIEnv *env, jclass fdClass) { fis_fd = (*env)->GetFieldID(env, fdClass, "fd", "Ljava/io/FileDescriptor;"); /* fd field,后面用來獲取 fd */ }
繼續(xù)調用readBytes
// ioutil.c jint readBytes(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ū)不足,動態(tà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,系統調用 */ 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); /* 失敗釋放內存 */ } return nread; }
我們繼續(xù)看看IO_Read的實現,是個宏定義
#define IO_Read handleRead
handleRead有兩種實現
solaris實現:
// /jdk/src/solaris/native/java/io/io_util_md.c ssize_t handleRead(FD fd, void *buf, jint len) { ssize_t result; RESTARTABLE(read(fd, buf, len), result); return result; } /* * Retry the operation if it is interrupted */ #define RESTARTABLE(_cmd, _result) do { \ do { \ _result = _cmd; \ } while((_result == -1) && (errno == EINTR)); \ /* 如果是中斷,則不斷重試,避免進程調度等待*/ } while(0)
read方法可以參考unix man page
windows實現:
// jdk/src/windows/native/java/io/io_util_md.c JNIEXPORT jint handleRead(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 JNICALL JNU_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); /* 調用JNI 接口*/ } /* JNU_Throw common exceptions */ JNIEXPORT void JNICALL JNU_ThrowNullPointerException(JNIEnv *env, const char *msg) { JNU_ThrowByName(env, "java/lang/NullPointerException", msg); }
最后是調用JNI:
// hotspot/src/share/vm/prims/jni.h jint ThrowNew(jclass clazz, const char *msg) { return functions->ThrowNew(this, clazz, msg); } jint (JNICALL *ThrowNew) (JNIEnv *env, jclass clazz, const char *msg);
4.總結
很多高級語言,有著不同的編程范式,但是歸根到底還是(c語言)系統調用,c語言能夠在更低的層面做非常多的優(yōu)化。如果我們了解了這些底層的系統調用,就能看到問題的本質。
到此這篇關于詳解Java的readBytes是怎么實現的的文章就介紹到這了,更多相關Java readBytes內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
實例分析Java中public static void main(String args[])是什么意思
這篇文章主要介紹了實例分析Java中public static void main(String args[])的意義,詳細分析了Java主函數main關鍵字聲明的具體含義和用法,需要的朋友可以參考下2015-12-12HttpUtils 發(fā)送http請求工具類(實例講解)
下面小編就為大家?guī)硪黄狧ttpUtils 發(fā)送http請求工具類(實例講解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07Spring?Security放行的接口Knife4j靜態(tài)資源的問題小結
這篇文章主要介紹了Spring?Security使用Knife4j靜態(tài)資源的問題小結,項目中使用?Spring?Security?做身份認證和授權,使用?Knife4j?做接口調試,需要?Spring?Security?放行的接口記錄在?RequestMatcherConstant?類中,感興趣的朋友跟隨小編一起看看吧2024-02-02IDEA在創(chuàng)建包時如何把包分開實現自動分層(方法詳解)
這篇文章主要介紹了IDEA在創(chuàng)建包時如何把包分開實現自動分層,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09SpringBoot實現發(fā)送郵件、發(fā)送微信公眾號推送功能
這篇文章主要介紹了SpringBoot實現發(fā)送郵件、發(fā)送微信公眾號推送功能,這里對成員變量JavaMailSender使用了@Resource注解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03