2012-01-05 34 views
6

我真的很陌生,因此,如果这听起来像一个愚蠢的问题,我提前道歉。OSX:JavaVM,AWT/Swing和可能的死锁

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

正在关注this技术说明我知道只有在Mac OSX中,必须从与主线程不同的线程调用JavaVM才能创建基于AWT的GUI。

因此,在我的C应用程序的main函数中,我创建了一个新线程来执行从javaVM创建到创建GUI的所有内容。

由于应用程序并非如此简单,我将发布一个简化版本。

主要功能:

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; 
} 

线程函数:

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); 

       } 
     }); 
    } 
} 

所有的O f除了创建导致死锁或类似情况的窗口之外,上述代码执行成功,因为终端保持繁忙状态,没有任何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 <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代码(感谢trashgod),但它仍然无法正常工作。 我错过了什么吗?

回答

7

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

有可能是您的特定实现一些额外的细节,但与此类似,目前在我们的案子:

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

+0

感谢提示,但链接选项对应的是什么? – 2013-12-27 08:16:20

+0

好的,谢谢,我自己找到了:'-framework CoreFoundation' – 2013-12-27 09:19:20

2

继此example之后,除非使用Cocoa,否则在C端不需要单独的线程。您需要使用invokeLater()event dispatch thread上构建您的Java GUI,您需要

+0

如果我不创建分离线程,则会出现以下错误:_Apple AWT Java VM在第一个线程上加载 - 无法启动AWT。 由于Java是在第一个线程上启动的,因此无法启动AWT。确保应用程序的Info.plist_中没有指定StartOnFirstThread。关于事件派发线程,我已经在昨天实现了它,但没有成功。也许我犯了一些错误..我会进一步阅读你发布的链接,我将发布修改后的代码。非常感谢您的回答。 – Andrea3000 2012-01-06 07:47:01

+0

我已经更新了添加invokeLater()的java代码,但没有任何更改。我是否用错误的方式实现了它? – Andrea3000 2012-01-07 14:11:28

+0

不是我能看到的。我重新运行了我的测试,并且出现类似的错误:“Apple AWT Java VM在第一个线程上加载 - 无法启动AWT。不幸的是,我没有看到有用的[解决方法](http:// www .google.com/search?q = Apple + AWT + Java + VM +已加载+ on + first + thread + - + can%27t + start + AWT)任何可以启动GUI和_then_加载C库? – trashgod 2012-01-07 15:04:00

2

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

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

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

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

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

我想发布这个,以防其他人在类似的情况下绊倒在这个帖子寻找答案。

+0

你能解释一下吗? – 2016-05-11 15:20:19