虚拟机加载so时间
chenk818
2012-03-26
java version "1.6.0_29"
java -Xbootclasspath/a:test.jar -jar XXX.jar 其中,test.jar中用到了jni,需要加载test.so,我把java.library.path输出后,有一系列的目录, ... /home/chenkui/allinstance/instance1/jdk/jre/lib/amd64 . (当前目录) .... /lib64 ... 其中,只有将test.so放到jdk/jre/lib/amd64下,能成功加载,放到当前目录却始终加载不上。我的理解是,虚拟机在使用so文件之前,必然已经将java.library.path下的so全加载完了,不应该跟目录的位置还有关系,求解答。。。 |
|
RednaxelaFX
2012-03-27
你的疑问大概来自这里?
/* sysclasspath, java_home, dll_dir */ { char *home_path; char *dll_path; char *pslash; char buf[MAXPATHLEN]; os::jvm_path(buf, sizeof(buf)); // Found the full path to libjvm.so. // Now cut the path to <java_home>/jre if we can. *(strrchr(buf, '/')) = '\0'; /* get rid of /libjvm.so */ pslash = strrchr(buf, '/'); if (pslash != NULL) *pslash = '\0'; /* get rid of /{client|server|hotspot} */ dll_path = malloc(strlen(buf) + 1); if (dll_path == NULL) return; strcpy(dll_path, buf); Arguments::set_dll_dir(dll_path); if (pslash != NULL) { pslash = strrchr(buf, '/'); if (pslash != NULL) { *pslash = '\0'; /* get rid of /<arch> */ pslash = strrchr(buf, '/'); if (pslash != NULL) *pslash = '\0'; /* get rid of /lib */ } } home_path = malloc(strlen(buf) + 1); if (home_path == NULL) return; strcpy(home_path, buf); Arguments::set_java_home(home_path); if (!set_boot_path('/', ':')) return; } #define DEFAULT_LIBPATH "/usr/lib64:/lib64:/lib:/usr/lib" /* * Where to look for native libraries * * Note: Due to a legacy implementation, most of the library path * is set in the launcher. This was to accomodate linking restrictions * on legacy Linux implementations (which are no longer supported). * Eventually, all the library path setting will be done here. * * However, to prevent the proliferation of improperly built native * libraries, the new path component /usr/java/packages is added here. * Eventually, all the library path setting will be done here. */ { char *ld_library_path; /* * Construct the invariant part of ld_library_path. Note that the * space for the colon and the trailing null are provided by the * nulls included by the sizeof operator (so actually we allocate * a byte more than necessary). */ ld_library_path = (char *) malloc(sizeof(REG_DIR) + sizeof("/lib/") + strlen(cpu_arch) + sizeof(DEFAULT_LIBPATH)); sprintf(ld_library_path, REG_DIR "/lib/%s:" DEFAULT_LIBPATH, cpu_arch); /* * Get the user setting of LD_LIBRARY_PATH, and prepended it. It * should always exist (until the legacy problem cited above is * addressed). */ char *v = getenv("LD_LIBRARY_PATH"); if (v != NULL) { char *t = ld_library_path; /* That's +1 for the colon and +1 for the trailing '\0' */ ld_library_path = (char *) malloc(strlen(v) + 1 + strlen(t) + 1); sprintf(ld_library_path, "%s:%s", v, t); } Arguments::set_library_path(ld_library_path); } 你的程序有没有显式设置过-Djava.library.path?或者LD_LIBRARY_PATH? 默认构造出来的路径,在Linux/AMD64版的Oracle JDK里就是上面代码那样的。最终拼接出来的ld_library_path就会作为后面系统属性里的java.library.path的值;除非你覆盖过这个值(显式设置了-Djava.library.path)。 同样,上面的dll_path最终会转变为sun.boot.library.path,除非通过-D显式覆盖掉这个值。 然后你的test.jar里是如何加载test.so的?是System.loadLibrary("test");么?那个的内部实现是这样的: // Invoked in the java.lang.Runtime class to implement load and loadLibrary. static void loadLibrary(Class fromClass, String name, boolean isAbsolute) { try { if (!DownloadManager.isJREComplete() && !DownloadManager.isCurrentThreadDownloading()) { DownloadManager.downloadFile("bin/" + System.mapLibraryName(name)); // it doesn't matter if the downloadFile call returns false -- // it probably just means that this is a user library, as // opposed to a JRE library } } catch (IOException e) { throw new UnsatisfiedLinkError("Error downloading library " + name + ": " + e); } catch (NoClassDefFoundError e) { // This happens while Java itself is being compiled; DownloadManager // isn't accessible when this code is first invoked. It isn't an // issue, as if we can't find DownloadManager, we can safely assume // that additional code is not available for download. } ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader(); if (sys_paths == null) { usr_paths = initializePath("java.library.path"); sys_paths = initializePath("sun.boot.library.path"); } if (isAbsolute) { if (loadLibrary0(fromClass, new File(name))) { return; } throw new UnsatisfiedLinkError("Can't load library: " + name); } if (loader != null) { String libfilename = loader.findLibrary(name); if (libfilename != null) { File libfile = new File(libfilename); if (!libfile.isAbsolute()) { throw new UnsatisfiedLinkError( "ClassLoader.findLibrary failed to return an absolute path: " + libfilename); } if (loadLibrary0(fromClass, libfile)) { return; } throw new UnsatisfiedLinkError("Can't load " + libfilename); } } for (int i = 0 ; i < sys_paths.length ; i++) { File libfile = new File(sys_paths[i], System.mapLibraryName(name)); if (loadLibrary0(fromClass, libfile)) { return; } } if (loader != null) { for (int i = 0 ; i < usr_paths.length ; i++) { File libfile = new File(usr_paths[i], System.mapLibraryName(name)); if (loadLibrary0(fromClass, libfile)) { return; } } } // Oops, it failed throw new UnsatisfiedLinkError("no " + name + " in java.library.path"); } private static boolean loadLibrary0(Class fromClass, final File file) { Boolean exists = (Boolean) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new Boolean(file.exists()); } }); if (!exists.booleanValue()) { return false; } String name; try { name = file.getCanonicalPath(); } catch (IOException e) { return false; } ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader(); Vector libs = loader != null ? loader.nativeLibraries : systemNativeLibraries; synchronized (libs) { int size = libs.size(); for (int i = 0; i < size; i++) { NativeLibrary lib = (NativeLibrary)libs.elementAt(i); if (name.equals(lib.name)) { return true; } } synchronized (loadedLibraryNames) { if (loadedLibraryNames.contains(name)) { throw new UnsatisfiedLinkError ("Native Library " + name + " already loaded in another classloader"); } /* If the library is being loaded (must be by the same thread, * because Runtime.load and Runtime.loadLibrary are * synchronous). The reason is can occur is that the JNI_OnLoad * function can cause another loadLibrary invocation. * * Thus we can use a static stack to hold the list of libraries * we are loading. * * If there is a pending load operation for the library, we * immediately return success; otherwise, we raise * UnsatisfiedLinkError. */ int n = nativeLibraryContext.size(); for (int i = 0; i < n; i++) { NativeLibrary lib = (NativeLibrary) nativeLibraryContext.elementAt(i); if (name.equals(lib.name)) { if (loader == lib.fromClass.getClassLoader()) { return true; } else { throw new UnsatisfiedLinkError ("Native Library " + name + " is being loaded in another classloader"); } } } NativeLibrary lib = new NativeLibrary(fromClass, name); nativeLibraryContext.push(lib); try { lib.load(name); } finally { nativeLibraryContext.pop(); } if (lib.handle != 0) { loadedLibraryNames.addElement(name); libs.addElement(lib); return true; } return false; } } } #define JNI_LIB_PREFIX "lib" #define JNI_LIB_SUFFIX ".so" static void cpchars(jchar *dst, char *src, int n) { int i; for (i = 0; i < n; i++) { dst[i] = src[i]; } } JNIEXPORT jstring JNICALL Java_java_lang_System_mapLibraryName(JNIEnv *env, jclass ign, jstring libname) { int len; int prefix_len = (int) strlen(JNI_LIB_PREFIX); int suffix_len = (int) strlen(JNI_LIB_SUFFIX); jchar chars[256]; if (libname == NULL) { JNU_ThrowNullPointerException(env, 0); return NULL; } len = (*env)->GetStringLength(env, libname); if (len > 240) { JNU_ThrowIllegalArgumentException(env, "name too long"); return NULL; } cpchars(chars, JNI_LIB_PREFIX, prefix_len); (*env)->GetStringRegion(env, libname, 0, len, chars + prefix_len); len += prefix_len; cpchars(chars + len, JNI_LIB_SUFFIX, suffix_len); len += suffix_len; return (*env)->NewString(env, chars, len); } 于是你有没有试过把test.so改名为libtest.so? |
|
chenk818
2012-03-27
不好意思没写清楚,我的so文件命名是有加lib的。我先照你给的代码调试一下,thx
|
|
chenk818
2012-03-27
if (sys_paths == null) {
usr_paths = initializePath("java.library.path"); sys_paths = initializePath("sun.boot.library.path"); } 我试了下,放到usr_paths就是不行,必须得放到sys_paths下面。是不是跟我的jar包是在-Xbootclasspath/a:test.jar有关系呢? |
|
RednaxelaFX
2012-03-27
你说得没错。抱歉,我没仔细看这点。
由于你是把JAR包加在bootclasspath上的,这JAR包里的类的classloader会是bootstrap class loader,在Java一侧表现为loader是null。 于是上面的Java代码里if (loader != null)的分支都进不去,而唯一能执行的地方就是: for (int i = 0 ; i < sys_paths.length ; i++) { File libfile = new File(sys_paths[i], System.mapLibraryName(name)); if (loadLibrary0(fromClass, libfile)) { return; } } 这段。 |
|
chenk818
2012-03-27
那就引出另外一个问题:jvm是否允许用户自定义sys_paths,用户能否将自己的目录加到sys_paths里面呢?
|
|
RednaxelaFX
2012-03-27
chenk818 写道 那就引出另外一个问题:jvm是否允许用户自定义sys_paths,用户能否将自己的目录加到sys_paths里面呢?
答案是可以的。参考例子:https://gist.github.com/2214201 显式设置-Dsun.boot.library.path会在默认的路径后拼接自己设置的路径,而不会覆盖。 if (strcmp(key, "sun.boot.library.path") == 0) { PropertyList_unique_add(&_system_properties, key, value, true); return true; } VM处理参数的逻辑会对这个参数做特殊处理,使它采用append而不是overwrite的方式设置属性。 喵的,这些处理方式的坑真多… |