奇怪现象:Java/Swing操作导致本机库(JNA)中的访问冲突
各位开发者好 因为SO几乎总是对我的编程问题有帮助,所以我决定注册,并尝试解决我最近的问题。这真是一个奇怪的现象,我和我的同事都无法理解。很抱歉,我不能提供一个工作样本,但该项目是复杂的方式来分解它,并需要特定的硬件来正常运行它。所以我会尽力解释的奇怪现象:Java/Swing操作导致本机库(JNA)中的访问冲突,java,swing,access-violation,jna,Java,Swing,Access Violation,Jna,各位开发者好 因为SO几乎总是对我的编程问题有帮助,所以我决定注册,并尝试解决我最近的问题。这真是一个奇怪的现象,我和我的同事都无法理解。很抱歉,我不能提供一个工作样本,但该项目是复杂的方式来分解它,并需要特定的硬件来正常运行它。所以我会尽力解释的 我们项目的基础是本地库(在这种情况下是32位Windows C-DLL),它通过java应用程序()访问特定于项目的硬件。其目的是在Swing UI中管理和显示硬件的专有文件系统(通过USB连接)。这对我们来说是一种非常常见的项目配置,因为我们在Ja
我们项目的基础是本地库(在这种情况下是32位Windows C-DLL),它通过java应用程序()访问特定于项目的硬件。其目的是在Swing UI中管理和显示硬件的专有文件系统(通过USB连接)。这对我们来说是一种非常常见的项目配置,因为我们在Java应用程序中集成了许多本机库和驱动程序
摘要:用于枚举设备的单元测试工作正常。本机库的一个模块分配内存并用结构填充内存,每个结构都包含所连接设备的信息。这不是一个好的做法,但既然我们对这一部分没有任何影响,我们就必须这样做。我在Java/JNA中映射了这个结构,调用本机函数,将结构内容复制到Java传输类,并在控制台中打印它。很好用 现在,如果枚举设备时有UI操作处于活动状态,则本机库会因访问冲突而崩溃。即使此UI操作与库无关。JNA错误消息显示异常访问冲突(0xc0000005),因此研究显示为无效/空内存 以前有人遇到过这样的问题吗?我们当然从来没有这样做过。我花了好几天时间才把错误源缩小到代码的这一部分。当涉及本机库时,调试并不容易。 是否可能存在JVM内存并发问题?由于本机库自己分配内存,而JVM对此一无所知,所以JVM会尝试在已经使用的内存中为新的Swing组件分配内存 代码: 下面的代码片段来自我的单元测试,尽可能地细分。预期的顺序很明显:从根节点删除节点,加载连接的设备,并将这些设备添加为新节点。这段代码会因访问冲突而崩溃,但不会在本机调用时崩溃——只要我访问树组件,它就会崩溃 public void loadDevices(){
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
rootNode.removeAllChildren();
rootNode.add(new LoadingNode());
tree.expandPath(new TreePath(rootNode));
}
});
final List<Device> devices = lib.loadDevices(); // wrapped native call
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
rootNode.removeAllChildren();
if(!devices.isEmpty()){
for (Device dev : devices ) {
DevNode node = new DevNode(dev);
rootNode.add(node);
}
}
}
});
}
public void loadDevices(){
SwingUtilities.invokeLater(新的Runnable(){
@凌驾
公开募捐{
rootNode.removeAllChildren();
添加(新加载节点());
expandPath(新树路径(根节点));
}
});
final List devices=lib.loadDevices();//包装的本机调用
SwingUtilities.invokeLater(新的Runnable(){
@凌驾
公开募捐{
rootNode.removeAllChildren();
如果(!devices.isEmpty()){
用于(设备开发:设备){
DevNode节点=新的DevNode(dev);
添加(node);
}
}
}
});
}
注意:DevNode不包含任何本机数据,每个本机结构的内容都复制到Java传输对象。GC在尝试移动对象数据时不应该出现问题,因为所有非托管代码都是在lib#loadDevices()方法中本地处理的
当我完全删除对SwingUtilities的调用并将生成的设备信息打印到控制台而不是创建节点时,这部分工作正常。
一旦我试图访问JTree或TreeModel成员,代码就会崩溃。如果我在调用swingutilies#invokeLater()或在同一个线程中执行此操作,则不起作用
我知道这是一个非常具体的问题,几乎没有人会感兴趣(是什么使得在SO/Google中搜索解决方案变得非常困难)。但也许我很幸运,有人已经遇到了这个问题
再见桑德 编辑:最初,此代码被包装在工作线程中,导致相同的结果。这只是我的单元测试的一个片段 编辑2:我好像说得不够清楚,或者忘了在这里提到一些重要的事情,对不起。对树或其模型的访问不一定与本机库有关。再看一遍代码:对invokeLater的第一次调用只会从树中删除节点。即使我删除了对invokeLater的第二个调用,本机库也会崩溃
final List devices=lib.loadDevices();
final List<Device> devices = lib.loadDevices();
- (假设使用JNA的代码能够单独返回每个节点,或者在结束时一次返回
集合)应该从工作线程调用,例如Runnable#Thread或SwingWorker列表的
- 直接(或通过创建新的MutableTreeNode)添加到DefaultTreeModel的to工作线程的所有输出都应包装到invokeLater中
- 如果不发布(
可能是USB端口列表),短的、可运行的、可编译的,大约是JTree,是模型,JNI/JNA列表的
setRoot(TreeNode root)
removeNodeFromParent(MutableTreeNode node)
insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index)
编辑节点本身可能(并且迟早会)导致奇怪的行为
当您使用DefaultTreeModel和MutableTreeNode时,当然会出现这种情况。我强烈建议您这样做,因为我已经看到许多错误的TreeModel实现。您是否有机会将侦听器连接到
JTree
/TreeModel
,从而触发本机代码?感谢您的快速响应!不,是这样