生活工程体验信仰哲学精神
投稿投诉
精神世界
探索历史
哲学文学
艺术价值
信仰创造
境界审美
体验技术
技能工具
工程信息
医学生产
生活运用
操作能力

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

7月3日 眸中星投稿
  前言
  无论是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(C?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(C?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(C?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))){}thrownewUnsatisfiedLinkError(Cantloadlibrary:name);}if(loader!null){Stringlibfilenameloader。findLibrary(name);if(libfilename!null){FilelibfilenewFile(libfilename);if(!libfile。isAbsolute()){thrownewUnsatisfiedLinkError(。。。);}if(loadLibrary0(fromClass,libfile)){}thrownewUnsatisfiedLinkError(Cantloadlibfilename);}}for(inti0;isyspaths。i){FilelibfilenewFile(syspaths〔i〕,System。mapLibraryName(name));if(loadLibrary0(fromClass,libfile)){}libfileClassLoaderHelper。mapAlternativeName(libfile);if(libfile!nullloadLibrary0(fromClass,libfile)){}}if(loader!null){for(inti0;iusrpaths。i){FilelibfilenewFile(usrpaths〔i〕,System。mapLibraryName(name));if(loadLibrary0(fromClass,libfile)){}libfileClassLoaderHelper。mapAlternativeName(libfile);if(libfile!nullloadLibrary0(fromClass,libfile)){}}}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。pathS。。。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);}
  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){}}路径中全找遍了,仍未找到则返回}
  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长度变量并获取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名称将后缀。so的字符拷贝到临时的char数组里的lib名称后cpchars(charslen,JNILIBSUFFIX,suffixlen);再次更新名称长度为:前缀lib名称后缀从char数组里提取当前长度的复数char成员到新创建的String对象中返回return(env)NewString(env,chars,len);}staticvoidcpchars(jchardst,charsrc,intn){for(i0;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);returnallNativeLibraryD}Listofapplicationnativelibrarydirectories。privatefinalListFilenativeLibraryDListofsystemnativelibrarydirectories。privatefinalListFilesystemNativeLibraryD
  应用自身的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)){}}catch(ErrnoExceptionignored){}}result。add(newFile(path));}}}
  系统列表则来自于系统的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);}}
  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)){returnentryP}}elseif(urlHandler!null){zip目录并且urlHandler都存在创建该zip目录下lib文件的完整名称StringentryNamezipD如果该名称的压缩包是否存在的话if(urlHandler。isEntryStored(entryName)){返回:路径zip目录lib名称的结果出去returnpath。getPath()zipSeparatorentryN}}}主要是确保在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(C?fromClass,finalFilefile){获取是否是内置动态链接库StringnameNativeLibrary。findBuiltinLib(file。getName());booleanisBuiltin(name!null);if(!isBuiltin){不是内置的话,检查文件是否存在booleanexistsAccessController。doPrivileged(newPrivilegedActionObject(){publicObjectrun(){returnfile。exists()?Boolean。TRUE:}})!if(!exists){}try{namefile。getCanonicalPath();}catch(IOExceptione){}}ClassLoaderloader(fromClassnull)?null:fromClass。getClassLoader();VectorNativeLibrarylibsloader!null?loader。nativeLibraries:systemNativeLsynchronized(libs){intsizelibs。size();检查是否已经加载过for(inti0;i){NativeLibraryliblibs。elementAt(i);if(name。equals(lib。name)){}}synchronized(loadedLibraryNames){再次检查所有library加载历史中是否存在if(loadedLibraryNames。contains(name)){thrownewUnsatisfiedLinkError(。。。);}intnnativeLibraryContext。size();检查是否已经在加载中了for(inti0;i){NativeLibrarylibnativeLibraryContext。elementAt(i);if(name。equals(lib。name)){if(loaderlib。fromClass。getClassLoader()){}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);}}}}
  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){charlibN。。。检查名称是否为空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);}free(libName);returnNULL;}
  findJniFunction()
  findJniFunction()用于到library指针、已加载卸载的JNI数组中查找该library名称所对应的JNIONLOAD、JNIONUNLOAD的函数地址。
  nativejavalangClassLoader。cstaticvoidfindJniFunction(JNIEnvenv,voidhandle,constcharcname,jbooleanisLoad){constcharonLoadSymbols〔〕JNIONLOADSYMBOLS;constcharonUnloadSymbols〔〕JNIONUNLOADSYMBOLS;voidentryNameNULL;。。。如果是加载,则到JNIONLOADSYMBOLS中获取函数数组和长度if(isLoad){symsonLoadSsymsLensizeof(onLoadSymbols)sizeof(char);}else{反之,则到JNIONUNLOADSYMBOLS中获取卸载函数数组和长度symsonUnloadSsymsLensizeof(onUnloadSymbols)sizeof(char);}遍历该数组,调用JVMFindLibraryEntry()逐个查找JNIOn(Un)Loadlibnamefunction是否存在for(i0;isymsLi){cnamesymif((len(cname!NULL?strlen(cname):0)strlen(syms〔i〕)2)FILENAMEMAX){}jniFunctionNamemalloc(len);if(jniFunctionNameNULL){JNUThrowOutOfMemoryError(env,NULL);}buildJniFunctionName(syms〔i〕,cname,jniFunctionName);entryNameJVMFindLibraryEntry(handle,jniFunctionName);free(jniFunctionName);if(entryName){}}done:如果没有找到,默认返回NULLreturnentryN}
  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{privateintjniVprivatefinalC?fromCSbooleanisBnativevoidload(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){。。。if(!initIDs(env))cnameJNUGetStringPlatformChars(env,name,0);if(cname0)handleisBuiltin?procHandle:JVMLoadLibrary(cname);if(handle){JNIOnLoadtJNIOnLJNIOnLoad(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);}}(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);}}(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〕;{ThreadToNativeFromVMttnfvm(thread);loadresultos::dllload(name,ebuf,sizeofebuf);}if(loadresultNULL){charmsg〔1024〕;jiosnprintf(msg,sizeofmsg,s:s,name,ebuf);HandlehexceptionExceptions::newexception(。。。);THROWHANDLE0(hexception);}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());}}voiddodlopen(。。。){。。。if(si!nullptr){voidhandlesitohandle();sicallconstructors();failureguard。Disable();}}
  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
投诉 评论

卖挂票(对口相声)甲做个京剧演员可不容易。乙嗳!得下苦功夫。甲还得有演员的材料,像聋子、哑巴能演戏吗?乙那是没法演戏。甲七年坐科,十几年舞台实践,不是一……三大原则教你判断什么牌子的护肤品美白效果好在文学作品、童话故事中,黑和白的符号特征很明显,比如白雪公主和灰姑娘,从名字上就可以看出谁更受作者喜欢。艺术来源于生活,之所以文学作品中有此特征,主要是因为在日常生活中肤白貌美……只需一篇就让你详细了解Java中so文件的加载原理前言无论是Android开发者还是Java工程师应该都有使用过JNI开发,但对于JVM如何加载so、Android系统如何加载so,可能鲜有时间了解。本文通过代码、……教育惩戒的实施考验着教师的智慧近日,中国青年报社社会调查中心对2005名受访者进行的一项调查显示:68。2的受访者坦言现在中小学教师不敢严管学生的情况普遍,受访的中小学生家长对此感受更为明显(72。9);受……女性创业者的优势表现在哪些方面对于很多职场女性来说,职场也许已经不能满足她们的需求,她们想要的更多,就是通过自己的创业去获得更好的一个发展。但是又担心自己的不足之处,创业一直没有推进。其实,告诉大家,不要总……工作中的项基本能力自我管理1。高兴才能做好乐观力在我们部门中有几个前辈,年纪轻轻的头上已经有白头发了,有时候会开玩笑的说,干设计这行压力太大了。其实不管我们从事的何种工作,都会不可避……吉林省大学排名吉林省最好大学排名强根据2020年校友会公布的最新大学排名数据榜单显示,吉林省共有2所高校跻身校友会2020中国大学排名100强,吉林大学、东北师范大学、长春理工大学雄居2020吉林省大学排名前3……明日2月13号,中到大雪大雨范围已确认,60年一遇冷春?农谚头条创作挑战赛导读:明日2月13号,中到大雪大雨范围已确认,60年一遇冷春?看农谚咋说。时光匆匆,不知不觉一年一度的春天还是如约而至,今天是农历正月二十二,2月12号,是……曲靖,球进!国际高原体育城曲靖掀起足球热珠江网讯(记者朱洪良)二月的曲靖春光明媚,宜步体育公园内,近500名来自云南省8个州市的足球小将们挥汗如雨,争夺2022年云南省青少年足球联赛的终极荣誉。曲靖,曲靖方言为……辅助生殖技术将纳入医保!2023年试管婴儿可以报销吗?附报销虽说现在三胎政策已经放开了,但由于各方面原因,很多女性面临怀孕难的情况,为此不得不选择高额的辅助生殖技术,而在近日,官方表示辅助生殖技术将纳入医保!那2023年试管婴儿可以报销……珍珠膏的功效与作用珍珠膏的正确用法珍珠膏的功效与作用珍珠膏有美白的功效,这是珍珠膏最显著的功效,珍珠膏含有丰富的准朱精华成分,能促进肌肤的新陈代谢,减少黑色素的生成以达到美白的效果。珍珠膏还可以抗老抗皱和……公元年历史大事件公元627年正月(丁亥唐太宗李世民贞观元年正月)改元。更定律令命吏部尚书长孙无忌与法官更议定律令,放宽绞刑五十条,改为加役流(流配而加役作),徙三千里,居作三……
人眼睛的颜色榜眼秀重伤,往后NBA球员还能打野球吗51股半年报业绩增长超500世界杯战袍品牌三分天下,耐克阿迪彪马场外掰手腕,这次谁会笑到快船121114险胜爵士,决战时刻换下莱昂纳德成为比赛取胜关野性霸气干净社会句子水泄造句用水泄造句大全伊朗国家队遭史上最惨世界杯失利民众为何载歌载舞上街庆祝?到底是科技成就了人类,还是终将毁灭人类?我的校园生活揭秘秦始皇自称祖龙之迷其中竟隐藏玄机有种格局,叫装穷小学环保作文沙漠中的沉思师德师风建设工作计划读书节的收获2016年高考作文预测题目美丽的乌镇英雄院士默默无名,穿7元廉价衣服,所干事业让中国导弹进步20波尔多红酒五年级上册滴水穿石的启示教学设计如何关掉浏览器阻止程序的功能?防止拦截三年级状物作文蛋宝宝摔碎了350字手机相册里的照片误删怎么恢复岳云鹏下半年综艺演出一律取消,李诞和于谦的谈话,透漏了真相初中励志日志1000字

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找