OSX:JavaVM,AWT / Swing以及可能的死锁

我是java编程的新手,所以如果这听起来像个愚蠢的问题,我会提前道歉。

我正在尝试构建一个用简单C编写的简单应用程序,它必须创建一个JavaVM ,然后通过加载基于AWT/Swing Java代码来创建一个新窗口。

按照本技术说明我了解到,仅在Mac OSX中,必须从与主线程不同的线程调用JavaVM,以便能够基于AWT创建GUI。

因此,在我的C应用程序的mainfunction中,我创建了一个执行所有操作的新线程,从创建javaVM到创建GUI。

由于应用程序实际上并不那么简单,我将发布一个简化版本。

主function:

 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\n", dlerror()); } else { printf("library JavaVM loaded\n"); } /* 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; } 

线程function:

 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代码:(更新)

 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的输出:

 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  (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118) - locked  (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  (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  (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  (a java.util.Vector) - locked  (a java.util.Vector) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1724) at java.lang.Runtime.loadLibrary0(Runtime.java:823) - locked  (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.(DebugHelper.java:29) at java.awt.Component.(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代码(感谢trashgod),但它仍然无效。 我错过了什么吗?

我能够通过查看Eclipse项目如何创建其启动器来解决此问题。 您需要为JVM生成一个单独的线程,但主要方法需要启动CFRunLoop。

您的特定实现可能还有一些其他详细信息,但与此类似的内容目前在我们的案例中有效:

 ... #include  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

在此示例之后 ,除非您使用Cocoa,否则在C端不需要单独的线程。 您需要使用invokeLater()在事件派发线程上构建Java GUI。

我有同样的问题,如果我在AWT之前加载我的本机库,那么它会挂起。 解决方案是在加载我的本机库之前加载AWT本机库。

 ColorModel.getRGBdefault(); //static code in ColorModel loads AWT native library System.loadLibrary("MyLibrary"); //then load your native code 

这实际上并没有解决原始海报的问题,但我在尝试解决类似问题时找到了他/她的post。 在我的例子中,我需要启动一个c ++程序,让它调用一个用Java编写的映像库。 该库使用了一些awt类,所以我看到了死锁问题,即使我没有在Java代码中创建UI。

另外,我想在不同的平台上编译相同的c ++代码,因此避免使用Cocoa。

因为我不需要创建Java UI,所以当我从c ++代码启动时,我可以将“-Djava.awt.headless = true”添加为jvm的选项。

我想发布这个,以防其他类似情况的人偶然发现这个post寻找答案。