专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

只需一篇就让你详细了解Java中so文件的加载原理

  前言
  无论是Android开发者还是Java工程师应该都有使用过JNI开发,但对于JVM如何加载so、Android系统如何加载so,可能鲜有时间了解。
  本文通过代码、流程解释,带大家快速了解其加载原理,扫清困惑。
  Systemload()loadLibrary()
  load()
  System提供的load()用于指定so的完整的路径名且带文件后缀并加载,等同于调用Runtime类提供的load()。
  Ifthefilenameargument,whenstrippedofanyplatformspecificlibraryprefix,path,andfileextension,indicatesalibrarywhosenameis,forexample,L,andanativelibrarycalledLisstaticallylinkedwiththeVM,thentheJNIOnLoadLfunctionexportedbythelibraryisinvokedratherthanattemptingtoloadadynamiclibrary。
  Eg。
  System。load(sdcardpathlibA。so)
  步骤简述:
  通过Reflection获取调用来源的Class实例接着调用Runtime的load0()实现
  load0()首先获取系统的SecurityManager。当SecurityManager存在的话检查目标so文件的访问权限,权限不足的话打印拒绝信息、抛出SecurityException,如果name参数为空,抛出NullPointerException。如果so文件名非绝对路径的话,并不支持,并抛出UnsatisfiedLinkError,message为:
  Expectinganabsolutepathofthelibrary:xxx
  针对so文件的权限检查和名称检查均通过的话,继续调用ClassLoader的loadLibrary()实现,需要留意的是绝对路径参数为true。
  javalangSystem。javapublicstaticvoidload(Stringfilename){Runtime。getRuntime()。load0(Reflection。getCallerClass(),filename);}javalangRuntime。javasynchronizedvoidload0(Classlt;?fromClass,Stringfilename){SecurityManagersecuritySystem。getSecurityManager();if(security!null){security。checkLink(filename);}if(!(newFile(filename)。isAbsolute())){thrownewUnsatisfiedLinkError(Expectinganabsolutepathofthelibrary:filename);}ClassLoader。loadLibrary(fromClass,filename,true);}
  loadLibrary()
  System类提供的loadLibrary()用于指定so的名称并加载,等同于调用Runtime类提供的loadLibrary()。在Android平台系统会自动去系统目录(systemlib64)、应用lib目录(dataappxxxlib64)下去找libname参数拼接了lib前缀的库文件。
  Thelibnameargumentmustnotcontainanyplatformspecificprefix,fileextensionorpath。
  IfanativelibrarycalledlibnameisstaticallylinkedwiththeVM,thentheJNIOnLoadlibnamefunctionexportedbythelibraryisinvoked。
  Eg。
  System。loadLibrary(A)
  步骤简述:
  同样通过Reflection获取调用来源的Class实例接着调用Runtime的loadLibrary0()实现
  loadLibrary0()首先获取系统的SecurityManager,并检查目标so文件的访问权限。权限不足或文件名为空的话和上面一样抛出Exception。确保so名称不包含,反之抛出UnsatisfiedLinkError,message为:
  Directoryseparatorshouldnotappearinlibraryname:xxx
  检查通过后,同样调用ClassLoader的loadLibrary()实现继续下一步,只不过绝对路径参数为false。
  javalangSystem。javapublicstaticvoidloadLibrary(Stringlibname){Runtime。getRuntime()。loadLibrary0(Reflection。getCallerClass(),libname);}javalangRuntime。javasynchronizedvoidloadLibrary0(Classlt;?fromClass,Stringlibname){SecurityManagersecuritySystem。getSecurityManager();if(security!null){security。checkLink(libname);}if(libname。indexOf((int)File。separatorChar)!1){thrownewUnsatisfiedLinkError(Directoryseparatorshouldnotappearinlibraryname:libname);}ClassLoader。loadLibrary(fromClass,libname,false);}
  ClassLoaderloadLibrary()
  上面的调用栈可以看到无论是load()还是loadLibrary()最终都是调用ClassLoader的loadLibrary(),主要区别在于name参数是lib完整路径、还是lib名称,以及是否是绝对路径参数。
  1。首先通过getClassLoader()获得加载源所属的ClassLoader实例
  2。确保存放libraries路径的字符串数组syspaths不为空。尚且为空的话,调用initializePath(java。library。path)先初始化usr路径字符串数组,再调用initializePath(sun。boot。library。path)初始化system路径字符串数组。initializePath()具体见下章节。
  3。依据是否isAbsolute决定是否直接加载library
  name是绝对路径的话,直接创建File实例,调用loadLibrary0(),继续加载该文件。具体见下章节。检查loadLibrary0的结果,true即表示加载成功,结束;false即表示加载失败,抛出UnsatisfiedLinkError
  Cantloadxxx
  name非绝对路径并且获取的ClassLoader存在的话,通过findLibrary(),根据so名称获得lib绝对路径,并创建指向该路径的File实例libfile。并确保该文件的路径是绝对路径。反之,抛出UnsatisfiedLinkError。
  ClassLoader。findLibraryfailedtoreturnanabsolutepath:xxx
  此后也是调用loadLibrary0()继续加载该文件,并检查loadLibrary0的结果,处理同上。
  4。假使ClassLoader不存在,遍历system路径字符串数组的元素。
  通过mapLibraryName()分别将libname映射到平台关联的lib完整名称并返回,具体见下章节。创建当前遍历的path下libfile实例。调用loadLibrary0()继续加载该文件,并检查结果:
  true则直接结束false的话,通过mapAlternativeName()获取该lib可能存在的替代文件名,比如将后缀替换为jnilib如果再度map后的libfile不为空,调用loadLibrary0()再度加载该文件并检查结果,true则直接结束;反之,进入下一次循环
  5。至此,如果仍未成功找到library文件,则在ClassLoader存在的情况下,到usr路径字符串数组中查找。遍历usr路径字符串数组的元素后续逻辑和上述一致,只是map时候的前缀不同,是usrpaths的元素
  6。最终进行默认处理,即抛出UnsatisfiedLinkError,提示在java。library。pathpropery代表的路径下也未找到so文件。
  noxxinjava。library。path
  javalangClassLoader。javastaticvoidloadLibrary(Classlt;?fromClass,Stringname,booleanisAbsolute){ClassLoaderloader(fromClassnull)?null:fromClass。getClassLoader();if(syspathsnull){usrpathsinitializePath(java。library。path);syspathsinitializePath(sun。boot。library。path);}if(isAbsolute){if(loadLibrary0(fromClass,newFile(name))){return;}thrownewUnsatisfiedLinkError(Cantloadlibrary:name);}if(loader!null){Stringlibfilenameloader。findLibrary(name);if(libfilename!null){FilelibfilenewFile(libfilename);if(!libfile。isAbsolute()){thrownewUnsatisfiedLinkError(。。。);}if(loadLibrary0(fromClass,libfile)){return;}thrownewUnsatisfiedLinkError(Cantloadlibfilename);}}for(inti0;isyspaths。length;i){FilelibfilenewFile(syspaths〔i〕,System。mapLibraryName(name));if(loadLibrary0(fromClass,libfile)){return;}libfileClassLoaderHelper。mapAlternativeName(libfile);if(libfile!nullloadLibrary0(fromClass,libfile)){return;}}if(loader!null){for(inti0;iusrpaths。length;i){FilelibfilenewFile(usrpaths〔i〕,System。mapLibraryName(name));if(loadLibrary0(fromClass,libfile)){return;}libfileClassLoaderHelper。mapAlternativeName(libfile);if(libfile!nullloadLibrary0(fromClass,libfile)){return;}}}Oops,itfailedthrownewUnsatisfiedLinkError(nonameinjava。library。path);}
  ClassLoaderinitializePath()
  从System中获取对应property代表的path到数组中。
  1。先调用getProperty()从JVM中取出配置的路径,默认的是。
  其中的checkKey()将检查key名称是否合法,null的话抛出NullPointerException:
  keycantbenull
  如果为,抛出IllegalArgumentException:
  keycantbeempty
  后面通过getSecurityManager()获取SecurityManager实例,检查是否存在该property的访问权限。
  2。如果允许引用路径元素并且存在的话,将路径字符串的char取出进行拼接、计算得到路径字符串数组。
  3。反之通过indexOf()统计出现的次数,并创建一个次数1的数组。
  4。遍历该路径字符串,通过substring()将各的中间path内容提取到上述数组中。
  5。最后返回得到的path数组。
  javalangClassLoader。javaprivatestaticString〔〕initializePath(Stringpropname){StringldpathSystem。getProperty(propname,);StringpsFile。pathSeparator;。。。ildpath。indexOf(ps);n0;while(i0){n;ildpath。indexOf(ps,i1);}String〔〕pathsnewString〔n1〕;ni0;jldpath。indexOf(ps);while(j0){if(ji0){paths〔n〕ldpath。substring(i,j);}elseif(ji0){paths〔n〕。;}ij1;jldpath。indexOf(ps,i);}paths〔n〕ldpath。substring(i,ldlen);returnpaths;}
  ClassLoaderfindLibrary()
  findLibrary()将到ClassLoader中查找lib,取决于各JVM的具体实现。比如可以看看Android上的实现。
  到DexPathList的具体实现中调用首先通过System类的mapLibraryName()中获得mapping后的lib全名,细节见下章节遍历存放nativelib路径元素数组nativeLibraryPathElements逐个调用各元素的findNativeLibrary()实现去寻找一经找到立即返回,遍历结束仍未发现的话返回null
  androidlibcoredalviksrcmainjavadalviksystemBaseDexClassLoader。javapublicStringfindLibrary(Stringname){returnpathList。findLibrary(name);}androidlibcoredalviksrcmainjavadalviksystemDexPathList。javapublicStringfindLibrary(StringlibraryName){到System中获得mapping后的lib全名StringfileNameSystem。mapLibraryName(libraryName);到存放nativelib路径数组中遍历for(NativeLibraryElementelement:nativeLibraryPathElements){Stringpathelement。findNativeLibrary(fileName);一旦找到立即返回并结束,反之进入下一次循环if(path!null){returnpath;}}路径中全找遍了,仍未找到则返回nullreturnnull;}
  SystemmapLibraryName()
  mapLibraryName()的作用很简单,即将lib名称mapping到完整格式的名称,比如输入opencv得到的是libopencv。so。如果遇到名称为空或者长度超上限240的话,将抛出相应Exception。
  javalangSystem。javapublicstaticnativeStringmapLibraryName(Stringlibname);
  其是native方法,具体实现位于JDKNativeSourceCode中,可在如下网站中看到,网站地址如下:
  http:hg。openjdk。java。netjdk8jdk8jdkfile687fd7c7986dsrcsharenative
  nativejavalangSystem。cdefineJNILIBPREFIXlibdefineJNILIBSUFFIX。soJavajavalangSystemmapLibraryName(JNIEnvenv,jclassign,jstringlibname){定义最后名称的Sring长度变量intlen;并获取lib前缀、后缀的字符串常量的长度intprefixlen(int)strlen(JNILIBPREFIX);intsuffixlen(int)strlen(JNILIBSUFFIX);定义临时的存放最后名称的char数组jcharchars〔256〕;如果libname参数为空,抛出NPEif(libnameNULL){JNUThrowNullPointerException(env,0);returnNULL;}获取libname长度len(env)GetStringLength(env,libname);如果大于240的话抛出IllegalArgumentExceptionif(len240){JNUThrowIllegalArgumentException(env,nametoolong);returnNULL;}将前缀lib的字符拷贝到临时的char数组头部cpchars(chars,JNILIBPREFIX,prefixlen);将lib名称从字符串里拷贝到char数组的lib后面(env)GetStringRegion(env,libname,0,len,charsprefixlen);更新名称长度为:前缀lib名称lenprefixlen;将后缀。so的字符拷贝到临时的char数组里的lib名称后cpchars(charslen,JNILIBSUFFIX,suffixlen);再次更新名称长度为:前缀lib名称后缀lensuffixlen;从char数组里提取当前长度的复数char成员到新创建的String对象中返回return(env)NewString(env,chars,len);}staticvoidcpchars(jchardst,charsrc,intn){inti;for(i0;in;i){dst〔i〕src〔i〕;}}
  逻辑很清晰,检查lib名称参数是否合法,之后便是将名称分别加上前后缀到临时字符数组中,最后转为字符串返回。
  nativeLibraryPathElements()
  nativeLibraryPathElements数组来源于获取到的所有nativeLibrary目录后转换而来。
  androidlibcoredalviksrcmainjavadalviksystemDexPathList。javapublicDexPathList(ClassLoaderdefiningContext,StringlibrarySearchPath){。。。this。nativeLibraryPathElementsmakePathElements(getAllNativeLibraryDirectories());}
  所有nativeLibrary目录除了包含应用自身的library目录列表以外,还包括了系统的列表部分。
  androidlibcoredalviksrcmainjavadalviksystemDexPathList。javaprivateListFilegetAllNativeLibraryDirectories(){ListFileallNativeLibraryDirectoriesnewArrayList(nativeLibraryDirectories);allNativeLibraryDirectories。addAll(systemNativeLibraryDirectories);returnallNativeLibraryDirectories;}Listofapplicationnativelibrarydirectories。privatefinalListFilenativeLibraryDirectories;Listofsystemnativelibrarydirectories。privatefinalListFilesystemNativeLibraryDirectories;
  应用自身的library目录列表来自于DexPathList初始化时传入的librarySearchPath参数,splitPaths()负责去该path下遍历各级目录得到对应数组。
  androidlibcoredalviksrcmainjavadalviksystemDexPathList。javapublicDexPathList(ClassLoaderdefiningContext,StringlibrarySearchPath){。。。this。nativeLibraryDirectoriessplitPaths(librarySearchPath,false);}privatestaticListFilesplitPaths(StringsearchPath,booleandirectoriesOnly){ListFileresultnewArrayList();if(searchPath!null){for(Stringpath:searchPath。split(File。pathSeparator)){if(directoriesOnly){try{StructStatsbLibcore。os。stat(path);if(!SISDIR(sb。stmode)){continue;}}catch(ErrnoExceptionignored){continue;}}result。add(newFile(path));}}returnresult;}
  系统列表则来自于系统的path路径,调用splitPaths()的第二个参数不同,促使其在分割的时候只处理目录类型的部分,纯文件的话跳过。
  androidlibcoredalviksrcmainjavadalviksystemDexPathList。javapublicDexPathList(ClassLoaderdefiningContext,StringlibrarySearchPath){。。。this。systemNativeLibraryDirectoriessplitPaths(System。getProperty(java。library。path),true);。。。}
  拿到path文件列表之后就是调用makePathElements转成对应元素数组。
  按照列表长度创建等长的Element数组遍历path列表如果path包含!的话,将其拆分为path和zipDir两部分,并创建NativeLibraryElement实例反之如果是目录的话,直接用path创建NativeLibraryElement实例,zipDir参数则为空
  androidlibcoredalviksrcmainjavadalviksystemDexPathList。javaprivatestaticNativeLibraryElement〔〕makePathElements(ListFilefiles){NativeLibraryElement〔〕elementsnewNativeLibraryElement〔files。size()〕;intelementsPos0;for(Filefile:files){Stringpathfile。getPath();if(path。contains(zipSeparator)){Stringsplit〔〕path。split(zipSeparator,2);FilezipnewFile(split〔0〕);Stringdirsplit〔1〕;elements〔elementsPos〕newNativeLibraryElement(zip,dir);}elseif(file。isDirectory()){Wesupportdirectoriesforlookingupnativelibraries。elements〔elementsPos〕newNativeLibraryElement(file);}}if(elementsPos!elements。length){elementsArrays。copyOf(elements,elementsPos);}returnelements;}
  findNativeLibrary()
  findNativeLibrary()将先确保当zip目录存在的情况下内部处理zip的ClassPathURLStreamHandler实例执行了创建。
  如果zip目录不存在(一般情况下都是不存在的)直接判断该路径下lib文件是否可读,YES则返回pathname、反之返回nullzip目录存在并且ClassPathURLStreamHandler实例也创建完毕的话,检查该name的zip文件的存在。并在YES的情况下,在path和name之间跟上zip目录并返回,即:path!zipDirname
  DexPathList。javaandroid。。。libcoredalviksrcmainjavadalviksystemDexPathList。javaprivatestaticfinalStringzipSeparator!;staticclassNativeLibraryElement{publicStringfindNativeLibrary(Stringname){确保element初始化完成maybeInit();if(zipDirnull){如果zip目录为空,则直接创建该path下该文件的File实例可读的话则返回StringentryPathnewFile(path,name)。getPath();if(IoUtils。canOpenReadOnly(entryPath)){returnentryPath;}}elseif(urlHandler!null){zip目录并且urlHandler都存在创建该zip目录下lib文件的完整名称StringentryNamezipDirname;如果该名称的压缩包是否存在的话if(urlHandler。isEntryStored(entryName)){返回:路径zip目录lib名称的结果出去returnpath。getPath()zipSeparatorentryName;}}returnnull;}主要是确保在zipDir不为空的情况下内部处理zip的urlHandler实例已经创建完毕publicsynchronizedvoidmaybeInit(){。。。}}
  ClassLoaderloadLibrary0()
  1。调用静态内部类NativeLibrary的native方法findBuiltinLib()检查是否是内置的动态链接库,细节见如下章节。如果不是内置的library,通过AccessController检查该library文件是否存在。
  不存在则加载失败并结束存在则到本ClassLoader已加载library的nativeLibrariesVector或系统class的已加载libraryVectorsystemNativeLibraries中查找是否加载过已加载过则结束反之继续加载的任务
  2。到所有ClassLoader已加载过的libraryVectorloadedLibraryNames里再次检查是否加载过,如果不存在的话,抛出UnsatisfiedLinkError:
  NativeLibraryxxxalreadyloadedinanotherclassloader
  3。到正在加载卸载library的nativeLibraryContextStack中检查是否已经处理中了
  存在并且ClassLoader来源匹配,则结束加载存在但ClassLoader来源不同,则抛出UnsatisfiedLinkError:NativeLibraryxxxisbeingloadedinanotherclassloader反之,继续加载的任务
  4。依据ClassLoader、library名称、是否内置等信息,创建NativeLibrary实例并入nativeLibraryContext栈。
  5。此后,交由NativeLibraryload,细节亦见如下章节,并在load后出栈。
  6。最后根据load的结果决定是否将加载记录到对应的Vector当中。
  javalangClassLoader。javaprivatestaticbooleanloadLibrary0(Classlt;?fromClass,finalFilefile){获取是否是内置动态链接库StringnameNativeLibrary。findBuiltinLib(file。getName());booleanisBuiltin(name!null);if(!isBuiltin){不是内置的话,检查文件是否存在booleanexistsAccessController。doPrivileged(newPrivilegedActionObject(){publicObjectrun(){returnfile。exists()?Boolean。TRUE:null;}})!null;if(!exists){returnfalse;}try{namefile。getCanonicalPath();}catch(IOExceptione){returnfalse;}}ClassLoaderloader(fromClassnull)?null:fromClass。getClassLoader();VectorNativeLibrarylibsloader!null?loader。nativeLibraries:systemNativeLibraries;synchronized(libs){intsizelibs。size();检查是否已经加载过for(inti0;isize;i){NativeLibraryliblibs。elementAt(i);if(name。equals(lib。name)){returntrue;}}synchronized(loadedLibraryNames){再次检查所有library加载历史中是否存在if(loadedLibraryNames。contains(name)){thrownewUnsatisfiedLinkError(。。。);}intnnativeLibraryContext。size();检查是否已经在加载中了for(inti0;in;i){NativeLibrarylibnativeLibraryContext。elementAt(i);if(name。equals(lib。name)){if(loaderlib。fromClass。getClassLoader()){returntrue;}else{thrownewUnsatisfiedLinkError(。。。);}}}创建NativeLibrary实例继续加载NativeLibrarylibnewNativeLibrary(fromClass,name,isBuiltin);并在加载前后压栈和出栈nativeLibraryContext。push(lib);try{lib。load(name,isBuiltin);}finally{nativeLibraryContext。pop();}加载成功的将该library名称缓存到vector中if(lib。loaded){loadedLibraryNames。addElement(name);libs。addElement(lib);returntrue;}returnfalse;}}}
  findBuiltinLib()
  首先一如既往地先检查libraryname是否为空,为空则抛出ErrorNULLfilenamefornativelibrary将string类型的名称转为char指针,失败的话抛出OutOfMemoryError检查名称长度是否短于最起码的lib。so几位,失败的话返回NULL结束创建library名称指针libName并分配内存从char指针提取libxxx。so中xxx。so部分到libName中将libName中。so的。位置替换成调用findJniFunction()依据handle指针,library名称检查该library的JNIOnLoad()是否存在存在则释放libName内存并返回该函数地址反之,释放内存并返回NULL结束
  nativejavalangClassLoader。cJavajavalangClassLoader00024NativeLibraryfindBuiltinLib(JNIEnvenv,jclasscls,jstringname){constcharcname;charlibName;。。。检查名称是否为空if(nameNULL){JNUThrowInternalError(env,NULLfilenamefornativelibrary);returnNULL;}procHandlegetProcessHandle();cnameJNUGetStringPlatformChars(env,name,0);检查char名称指针是否为空if(cnameNULL){JNUThrowOutOfMemoryError(env,NULL);returnNULL;}检查名称长度lenstrlen(cname);if(len(prefixLensuffixLen)){JNUReleaseStringPlatformChars(env,name,cname);returnNULL;}提取library名称(取出前后缀)libNamemalloc(len1);1fornullifprefixsuffix0if(libNameNULL){JNUReleaseStringPlatformChars(env,name,cname);JNUThrowOutOfMemoryError(env,NULL);returnNULL;}if(lenprefixLen){strcpy(libName,cnameprefixLen);}JNUReleaseStringPlatformChars(env,name,cname);libName〔strlen(libName)suffixLen〕;检查JNIOnLoad()释放存在retfindJniFunction(env,procHandle,libName,JNITRUE);if(ret!NULL){libJNUNewStringPlatform(env,libName);free(libName);returnlib;}free(libName);returnNULL;}
  findJniFunction()
  findJniFunction()用于到library指针、已加载卸载的JNI数组中查找该library名称所对应的JNIONLOAD、JNIONUNLOAD的函数地址。
  nativejavalangClassLoader。cstaticvoidfindJniFunction(JNIEnvenv,voidhandle,constcharcname,jbooleanisLoad){constcharonLoadSymbols〔〕JNIONLOADSYMBOLS;constcharonUnloadSymbols〔〕JNIONUNLOADSYMBOLS;voidentryNameNULL;。。。如果是加载,则到JNIONLOADSYMBOLS中获取函数数组和长度if(isLoad){symsonLoadSymbols;symsLensizeof(onLoadSymbols)sizeof(char);}else{反之,则到JNIONUNLOADSYMBOLS中获取卸载函数数组和长度symsonUnloadSymbols;symsLensizeof(onUnloadSymbols)sizeof(char);}遍历该数组,调用JVMFindLibraryEntry()逐个查找JNIOn(Un)Loadlibnamefunction是否存在for(i0;isymsLen;i){cnamesymif((len(cname!NULL?strlen(cname):0)strlen(syms〔i〕)2)FILENAMEMAX){gotodone;}jniFunctionNamemalloc(len);if(jniFunctionNameNULL){JNUThrowOutOfMemoryError(env,NULL);gotodone;}buildJniFunctionName(syms〔i〕,cname,jniFunctionName);entryNameJVMFindLibraryEntry(handle,jniFunctionName);free(jniFunctionName);if(entryName){break;}}done:如果没有找到,默认返回NULLreturnentryName;}
  JVMFindLibraryEntry()
  JVMFindLibraryEntry()调用的是平台相关的dlllookup(),依据library指针和function名称。
  vmprimsjvm。cppJVMLEAF(void,JVMFindLibraryEntry(voidhandle,constcharname))JVMWrapper2(JVMFindLibraryEntry(s),name);returnos::dlllookup(handle,name);JVMEND
  NativeLibraryload()
  NativeLibrary是定义在ClassLoader内的静态内部类,其代表着已加载library的实例,包含了该library的指针、所需的JNI版本、加载的Class来源、名称、是否是内置library、是否加载过重要信息。以及核心的加载load、卸载unloadnative实现。
  javalangClassLoader。javastaticclassNativeLibrary{longhandle;privateintjniVersion;privatefinalClasslt;?fromClass;Stringname;booleanisBuiltin;booleanloaded;nativevoidload(Stringname,booleanisBuiltin);nativevoidunload(Stringname,booleanisBuiltin);staticnativeStringfindBuiltinLib(Stringname);。。。}
  本章节我们着重看下load()的关键实现:
  1。首先调用initIDs()初始化ID等基本数据
  如果ClassLoaderNativeLibrary内部类、handle等属性有一不存在的话,返回FALSE并结束加载通过检查的话初始化procHandle指针
  2。其次通过JNUGetStringPlatformChars()将String类型的library名称转为char类型,如果名称为空的话结束加载
  3。如果不是内置的so,需要调用JVMLoadLibrary()加载得到指针(见下章节),反之沿用上述的procHandle指针即可
  4。如果so指针存在的话,通过findJniFunction()和指针参数获取JNIOnLoad()的地址。如果JNIOnLoad()获取成功,则调用它并得到该so要求的jniVersion。反之设置为默认值0x00010001,即JNIVERSION11,1。1。接着调用JVMIsSupportedJNIVersion()检查JVM是否支持该版本,调用的是Threads的issupportedjniversionincluding11()。如果不支持或者是内置so同时版本低于1。8,抛出UnsatisfiedLinkError:
  unsupportedJNIversionxxxrequiredbyyyy
  反之表示加载成功。
  5。反之,抛出异常ExceptionOccurred。
  nativejavalangClassLoader。cJavajavalangClassLoader00024NativeLibraryload(JNIEnvenv,jobjectthis,jstringname,jbooleanisBuiltin){constcharcname;。。。voidhandle;if(!initIDs(env))return;cnameJNUGetStringPlatformChars(env,name,0);if(cname0)return;handleisBuiltin?procHandle:JVMLoadLibrary(cname);if(handle){JNIOnLoadtJNIOnLoad;JNIOnLoad(JNIOnLoadt)findJniFunction(env,handle,isBuiltin?cname:NULL,JNITRUE);if(JNIOnLoad){。。。jniVersion(JNIOnLoad)(jvm,NULL);}else{jniVersion0x00010001;}。。。if(!JVMIsSupportedJNIVersion(jniVersion)(isBuiltinjniVersionJNIVERSION18)){charmsg〔256〕;jiosnprintf(msg,sizeof(msg),unsupportedJNIversion0x08Xrequiredbys,jniVersion,cname);JNUThrowByName(env,javalangUnsatisfiedLinkError,msg);if(!isBuiltin){JVMUnloadLibrary(handle);}gotodone;}(env)SetIntField(env,this,jniVersionID,jniVersion);}else{cause(env)ExceptionOccurred(env);if(cause){(env)ExceptionClear(env);(env)SetLongField(env,this,handleID,(jlong)0);(env)Throw(env,cause);}gotodone;}(env)SetLongField(env,this,handleID,ptrtojlong(handle));(env)SetBooleanField(env,this,loadedID,JNITRUE);done:JNUReleaseStringPlatformChars(env,name,cname);}staticjbooleaninitIDs(JNIEnvenv){if(handleID0){jclassthis(env)FindClass(env,javalangClassLoaderNativeLibrary);if(this0)returnJNIFALSE;handleID(env)GetFieldID(env,this,handle,J);if(handleID0)returnJNIFALSE;。。。procHandlegetProcessHandle();}returnJNITRUE;}
  JVMLoadLibrary()
  JVMLoadLibrary()是JVM这层加载library的最后一个实现,具体步骤如下:
  定义1024长度的char数组和接收加载结果的指针调用dllload()加载library,其细节见下章节加载失败的话,打印library名称和错误message同时抛出UnsatisfiedLinkError反之将加载结果返回
  vmprimsjvm。cppJVMENTRYNOENV(void,JVMLoadLibrary(constcharname))JVMWrapper2(JVMLoadLibrary(s),name);charebuf〔1024〕;voidloadresult;{ThreadToNativeFromVMttnfvm(thread);loadresultos::dllload(name,ebuf,sizeofebuf);}if(loadresultNULL){charmsg〔1024〕;jiosnprintf(msg,sizeofmsg,s:s,name,ebuf);HandlehexceptionExceptions::newexception(。。。);THROWHANDLE0(hexception);}returnloadresult;JVMEND
  dllload()
  dllload()的实现跟平台相关,比如bsd平台就是调用标准库的dlopen(),而其最终的结果来自于dodlopen(),其将通过findlibrary()得到soinfo实例,内部将执行tohandle()得到library的指针。
  bioniclibdllibdl。cppvoiddlopen(constcharfilename,intflag){constvoidcalleraddrbuiltinreturnaddress(0);returnloaderdlopen(filename,flag,calleraddr);}voidloaderdlopen(constcharfilename,intflags,constvoidcalleraddr){returndlopenext(filename,flags,nullptr,calleraddr);}staticvoiddlopenext(。。。){ScopedPthreadMutexLockerlocker(gdlmutex);glinkerlogger。ResetState();voidresultdodlopen(filename,flags,extinfo,calleraddr);if(resultnullptr){bionicformatdlerror(dlopenfailed,linkergeterrorbuffer());returnnullptr;}returnresult;}voiddodlopen(。。。){。。。if(si!nullptr){voidhandlesitohandle();sicallconstructors();failureguard。Disable();returnhandle;}returnnullptr;}
  JNIOnLoad()
  JNIOnLoad()定义在jni。h中,当library被JVM加载时会回调,该方法内一般会通过registerNatives()注册native方法并返回该library所需的JNI版本。该头文件还定义了其他函数和常量,比如JNI1。1等数值。
  jni。h。。。structJNIEnv{。。。jintRegisterNatives(jclassclazz,constJNINativeMethodmethods,jintnMethods){returnfunctionsRegisterNatives(this,clazz,methods,nMethods);}jintUnregisterNatives(jclassclazz){returnfunctionsUnregisterNatives(this,clazz);}}。。。Definedbynativelibraries。JNIEXPORTjintJNICALLJNIOnLoad(JavaVMvm,voidreserved);defineJNIVERSION110x00010001。。。
  结语
  总体流程可以归纳如下:
  System类提供的load()加载so的完整的路径名且带文件后缀,等同于直接调用Runtime类提供的load();loadLibrary()用于加载指定so的名称,等同于调用Runtime类提供的loadLibrary()。两者都将通过SecurityManager检查so的访问权限以及名称是否合法之后调用ClassLoader类的loadLibrary()实现,区别在于前者指定的是否是绝对路径的isAbsolute参数是否为trueClassLoader首先需要通过System提供的getProperty()获取JVM配置的存放usr、systemlibrary路径字符串数组如果libraryname非绝对路径,需要先调用findLibrary()获取该name对应的完整so文件,之后再调用loadLibrary0()继续当ClassLoader不存在,分别到system、usr字符串数组中查找该so是否存在loadLibrary0()将调用native方法findBuiltinLib()检查是否是内置的动态链接库,并到加载过vector、加载中context中查找是否已经加载过、加载中通过检查的话调用NativeLibrary静态内部类继续,事实上是调用ClassLoader。c的load()其将调用jvm。cpp的JVMLoadLibrary()进行so的加载获得指针根据OS的实现,dllload()通过dlopen()执行so的打开和地址返回最后通过findJniFunction()获取JNIOnLoad()地址进行native方法的注册和所需JNI版本的收集。
  原文链接:https:mp。weixin。qq。comsHVQvjDhhUuCrkBuOP8PJZw

你是否也会早上起床时有口臭?早上一醒来嘴巴里面会散发出一些难闻的气味,这是由于我们睡觉时嘴巴基本处于闭合状态,口水分泌减少,舌头的运动也基本静止了,这样就会降低口腔自我清洁能力,加上嘴里没清除掉的食物残渣被口广东球迷从不担心球队缺席季后赛昨天,由于众所周知的原因,广东与北控的比赛被判020告负。相信后续还有更多同样处理的比赛。赛前,自媒体上只有各种惟恐天下不乱,担心姚明会从轻处理诸如此类的,恨不得广东迎来史上最惨NCBA状元首秀绝地抢断,西热力江激动不已今年的CBA状元来自于清华大学,他是真正的文武双全,它的名字叫做王岚嵚!一米八三的身高,帅气的外表,而且球商极高。并且他是国内罕见的后场鬼才。由于篮球和学习双优,被保送进了清华大学下半场才算入局,广州队年轻球员距离挑大梁还很远进攻起色不大防守暴露两大问题,下半场才算入局广州队年轻球员距离挑大梁还很远视频加载中7轮不胜,这是广州队自从2011年升入中超以来最长的联赛不胜纪录,不过考虑到球队如今内忧外患处于中国斯诺克大捷,丁俊晖42完胜北京时间10月12日消息,2022斯诺克苏格兰公开赛资格赛,结束第三个比赛日的争夺。中国斯诺克军团发挥出色,中国一哥丁俊晖以41战胜艾利特斯莱瑟。另外,名将田鹏飞以40轻取菲戈尔奥又受伤!才刚刚签下长约啊!这个榜眼也要废了真惨啊,常规赛还没开始,这个榜眼又伤了在刚刚结束的一场季前赛,比赛刚开始不久,巴格利就在无对抗的情况下意外滑倒,随后在队友的搀扶下退出比赛。根据雅虎体育的记者Haynes爆料,巴格兵贵神速,广东宏远将出战江苏同曦大家好,我是璐璐!晚上好呀,今天给大家带来一则简讯刚刚球队方面终于传来好消息了,这一段时间接连的打击,都让璐璐快坚持不住了,幸好现在一切都开始变得越来越好,真的好开心!为了让所有的CBA这是要大变天啊看了四五场CBA的比赛,这个赛季应该比较好看了,整体感觉是强队不一定会强,弱队也有咸鱼翻身的了!1,上海对新疆这两只球队太让人羡慕了,人员齐整,感觉好手云集,教练都不知道该上谁好。伤害肾功能的五个凶手慢性肾小球肾炎在早期时,肾脏的主要威胁是尿蛋白升高,降低尿蛋白也是治疗的主要目标。而在进入到肾功能不全之后,损害肾脏的因素就更多了,不只是尿蛋白,我们要防范更多的因素损害肾功能。是清代名医的三味药,搞定了久治不愈的耳聋耳鸣,分清虚实最重要谈起耳朵所对应的脏腑,肾脏为大家所熟知,所以如果日常生活中,有人不经意间谈起,哎,最近我这耳朵不好使,总是不大能听清楚。往往便会有人答道,是不是你肾最近有些虚?补补肾?补肾确实是治狗尾巴草是个宝,坚持用它煮水喝,或能帮你解决五个健康问题狗尾草是一种常见的植物,公路边的草地上,乡间小路上以及农田的杂草里,只要有土的地方,就会有狗尾巴草的身影。狗尾草属于一年生草本植物,秆直立或基部弯曲,叶鞘松弛,无毛或疏具柔毛或疣毛
蒂塔万提斯现身活动,穿蓝色亮片裙华丽十足,短发干练气质好近日,美国纽约,蒂塔万提斯(Ditavonteese)前往电影亲爱的别担心首映式参加活动,提前预祝新电影大卖。当天,身穿蓝色亮片裙华丽十足,短发干练气质好。蒂塔万提斯(Ditavo北京环球度假区大片世界年度体验洞察首发布揭示高品质沉浸乐趣9月20日,北京环球度假区迎来盛大开园一周年。过去一年里,高品质的娱乐体验让万千游客在这里找到情感的连接,在电影世界里释放自我追逐快乐。去北京环球度假区代表着一种全新的生活方式,也二十岁是男孩子最无能为力的年纪前段时间去外地出差了几个月刚回来,就约了以前几个朋友见面喝酒,酒中中旬,两个人聊起现状和梦想,二十多岁的我们,是最好的年纪,但也是最无能的年纪,他一直跟我抱怨和吐槽在广州生活多难。情不可泛滥喜欢是一件好事,可是,喜欢很多人未必是一件好事。自古以来,择一人终老是一件很浪漫的事,不是因为钱财地位容貌而选择对方,而是因为三观人品道德选择对方。爱情中的情,就是一种情愫,不为肉能让松弛感常常伴随着你就是人生的赢家给人生一些松弛感松弛人呀,说起来从出生到死就没有过轻松,每一天都面临着意外和死亡。如果能活到老那就是最大的福份了,所以松弛感只是每个人内心的感觉,而这种感觉常常直接影响着你的身心健山西三处景点,后起之秀,十一佳节游玩胜地山西的旅游资源颇为丰富,尤其是近几年,随着当地政府对旅游业的重视度日益提高,使得当地的旅游业蓬勃发展,起到良好的效应,许多绝美的景区应运而生。尤其是以下这三处景点,实属后起之秀,颇又上九嵕山作者杜晓辉,字建辉,陕西武功人,中国作家协会会员中国散文学会会员。昨天,我们在昭陵博物馆参加完毕张艾的新书分享会后,在礼泉文友的邀请与陪同下,又一次去登九嵕山,上昭陵去看我的那位千骑行随笔002拍照,武警让,保安不让我和师妹约了午饭,在学校旁边的兰州拉面,12点。我先把车子停在了竹洞天,是个景区,但是现在已经是半荒废状态了,然后把自行车从车上拿下来。我骑自行车去,从日照城西骑过去。我先骑到老汽营销观察米老鼠牵手孙悟空,上海迪士尼乐园开了一家西游主题VR体验场馆上海迪士尼乐园在台风天到来前完成了一件大事。9月10日,米奇和米妮在巨型唐老鸭的见证下,为SoRealXR超体空间剪彩,欢迎孙悟空来到迪士尼小镇。全球首座在迪士尼度假区内运营的大型布局十年,鹰城终于过了这道坎每一座城市都是有记忆和灵性的。我们每走过一座城,这座城市留在我们记忆深处的都是一幅独一无二的画卷。八年来,政信人的脚步到访过平顶山市鲁山县宝丰县郏县等地,汇聚政信力量推进项目进程的2022西安旅游攻略,亲身体验西安旅游经历分享与景点路线推荐西安,承载了中国最多记忆的地方,是多少人自古以来的向往,踏入皇城,一日看尽长安花。西安的历史民俗美食都让人流连忘返西安我去过3次了,分享一下我上次我刚去西安的旅游攻略,希望对你有帮
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网