OSX: JavaVM, AWT/Swing and possibly a deadlock
我对Java编程真的很陌生,因此如果这听起来像一个愚蠢的问题,我会提前道歉。
我正在尝试构建一个用普通C语言编写的简单应用程序,该应用程序必须创建
遵循此技术说明,我了解到仅在Mac OSX中,必须从不同于主线程的线程中调用JavaVM,以便能够基于AWT创建GUI。
因此,在C应用程序的
由于该应用程序实际上并非如此简单,因此我将发布一个简化版本。
主功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | int main(int argc, char** argv) { // Run-time loading of JavaVM framework void *result; result = dlopen("/System/Library/Frameworks/JavaVM.framework/JavaVM", RTLD_LAZY); if (!result) { printf("can't open library JavaVM: %s\ ", dlerror()); } else { printf("library JavaVM loaded\ "); } /* Start the thread that runs the VM. */ pthread_t vmthread; // create a new pthread copying the stack size of the primordial pthread struct rlimit limit; size_t stack_size = 0; int rc = getrlimit(RLIMIT_STACK, &limit); if (rc == 0) { if (limit.rlim_cur != 0LL) { stack_size = (size_t)limit.rlim_cur; } } pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); if (stack_size > 0) { pthread_attr_setstacksize(&thread_attr, stack_size); } /* Start the thread that we will start the JVM on. */ pthread_create(&vmthread, &thread_attr, startJava, (void *)&thread_data_struct); pthread_attr_destroy(&thread_attr); pthread_exit(NULL); return 0; } |
线程功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | void *startJava(void *jvm_lib) { JavaVMInitArgs args; const char* classpath = getenv("CLASSPATH"); // determine classpath char* classpath_opt = str_printf("-Djava.class.path=%s", classpath); JavaVMOption* option = malloc(sizeof(JavaVMOption) * 2); option[0].optionString = classpath_opt; option[1].optionString = str_printf("-verbose:jni"); args.version = JNI_VERSION_1_6; args.nOptions = 2; args.options = option; args.ignoreUnrecognized = JNI_FALSE; // don't ignore unrecognized options fptr_JNI_CreateJavaVM JNI_CreateJavaVM_fp = (fptr_JNI_CreateJavaVM)dl_dlsym(jvm_lib, "JNI_CreateJavaVM"); int result = JNI_CreateJavaVM_fp(&jvm, (void**) &env, &args); free(option); free(classpath_opt); // launch java code jclass init_class = (*env)->FindClass(env,"org/classes/Loader"); jmethodID load_id = (*env)->GetStaticMethodID(env, init_class,"Load", "(Ljava/lang/String;Lorg/classes/stuff;J)V"); (*env)->CallStaticVoidMethod(env, init_class, load_id); } |
Java代码:(已更新)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package org.classes; import java.awt.AWTException; import java.awt.Component; import java.awt.Frame; import java.awt.image.BufferedImage; import java.awt.EventQueue; public class Loader { public static void Load(String baseDir, Stuff stuff, long nativePointer) { EventQueue.invokeLater(new Runnable() { public void run() { System.loadLibrary("drawingHelperLibrary"); ... ... ... // start test window Frame frame = new Frame(); frame.setSize(640,480); frame.setLocation(50, 50); frame.setVisible(true); } }); } } |
以上所有代码均成功执行,但创建窗口会导致死锁或类似情况,这是因为终端保持忙碌而没有占用任何CPU资源,并且两个线程均保持活动状态。
如果我注释掉有关创建窗口的几行,则应用程序将成功执行并退出。
这是jstack的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.4-b02-402 mixed mode): "Attach Listener" daemon prio=9 tid=1040b1800 nid=0x11b888000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Low Memory Detector" daemon prio=5 tid=103806000 nid=0x10b137000 runnable [00000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" daemon prio=9 tid=103805800 nid=0x10b034000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" daemon prio=9 tid=103804800 nid=0x10af31000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" daemon prio=9 tid=103804000 nid=0x10ae2e000 runnable [00000000] java.lang.Thread.State: RUNNABLE "Surrogate Locker Thread (Concurrent GC)" daemon prio=5 tid=103803000 nid=0x10ad2b000 waiting on condition [00000000] java.lang.Thread.State: RUNNABLE "Finalizer" daemon prio=8 tid=10409b800 nid=0x10ac28000 in Object.wait() [10ac27000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <7f3001300> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked <7f3001300> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159) "Reference Handler" daemon prio=10 tid=10409b000 nid=0x10ab25000 in Object.wait() [10ab24000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <7f30011d8> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:485) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116) - locked <7f30011d8> (a java.lang.ref.Reference$Lock) "main" prio=5 tid=104000800 nid=0x10048d000 runnable [10048a000] java.lang.Thread.State: RUNNABLE at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1827) - locked <7f30010a8> (a java.util.Vector) - locked <7f3001100> (a java.util.Vector) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1724) at java.lang.Runtime.loadLibrary0(Runtime.java:823) - locked <7f3004e90> (a java.lang.Runtime) at java.lang.System.loadLibrary(System.java:1045) at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50) at java.security.AccessController.doPrivileged(Native Method) at sun.awt.NativeLibLoader.loadLibraries(NativeLibLoader.java:38) at sun.awt.DebugHelper.<clinit>(DebugHelper.java:29) at java.awt.Component.<clinit>(Component.java:566) at org.classes.Loader.Load(Loader.java:69) "VM Thread" prio=9 tid=104096000 nid=0x10aa22000 runnable "Gang worker#0 (Parallel GC Threads)" prio=9 tid=104002000 nid=0x103504000 runnable "Gang worker#1 (Parallel GC Threads)" prio=9 tid=104002800 nid=0x103607000 runnable "Concurrent Mark-Sweep GC Thread" prio=9 tid=10404d000 nid=0x10a6f0000 runnable "VM Periodic Task Thread" prio=10 tid=103817800 nid=0x10b23a000 waiting on condition "Exception Catcher Thread" prio=10 tid=104001800 nid=0x103401000 runnable JNI global references: 913 |
我真的不知道该怎么办。也许这是一个愚蠢的错误,但是我对Java-C混合并不熟练,因为这是我第一次看它。
更新:我已经更新了Java代码(感谢rashgod),但仍然无法正常工作。
我想念什么吗?
通过查看Eclipse项目如何创建启动器,我能够解决此问题。完成后,您需要为JVM生成一个单独的线程,但是main方法需要启动CFRunLoop。
您的特定实现可能还有一些其他细节,但是在我们的案例中,与此类似的东西目前正在起作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ... #include <CoreServices/CoreServices.h> static void dummyCallback(void * info) {} ... ... if (stack_size > 0) { pthread_attr_setstacksize(&thread_attr, stack_size); } CFRunLoopRef loopRef = CFRunLoopGetCurrent(); /* Start the thread that we will start the JVM on. */ pthread_create(&vmthread, &thread_attr, startJava, (void *)&thread_data_struct); pthread_attr_destroy(&thread_attr); CFRunLoopSourceContext sourceContext = { .version = 0, .info = NULL, .retain = NULL, .release = NULL, .copyDescription = NULL, .equal = NULL, .hash = NULL, .schedule = NULL, .cancel = NULL, .perform = &dummyCallback }; CFRunLoopSourceRef sourceRef = CFRunLoopSourceCreate(NULL, 0, &sourceContext); CFRunLoopAddSource(loopRef, sourceRef, kCFRunLoopCommonModes); CFRunLoopRun(); CFRelease(sourceRef); ... |
您可以在此处浏览Eclipse实现:
http://git.eclipse.org/c/equinox/rt.equinox.framework.git
我有同样的问题,如果我在AWT之前加载本机库,则它会挂起。解决方案是在加载本机库之前先加载AWT本机库。
1 2 | ColorModel.getRGBdefault(); //static code in ColorModel loads AWT native library System.loadLibrary("MyLibrary"); //then load your native code |
在此示例之后,除非使用Cocoa,否则在C端不需要单独的线程。您确实需要使用
这实际上并不能解决原始海报的问题,但是我在尝试解决类似问题时发现了他/她的帖子。就我而言,我需要启动一个c ++程序,并使它调用用Java编写的映像库。该库使用了一些awt类,因此即使没有在Java代码中创建UI,我也遇到了死锁问题。
另外,我想在不同平台上编译相同的c ++代码,因此避免使用Cocoa。
因为不需要创建Java UI,所以从c ++代码启动jvm时,它可以为我添加" -Djava.awt.headless = true"作为选项。
我想发布此信息,以防其他类似情况的人偶然发现此信息以寻找答案。