首页 > 解决方案 > java.lang.Error:32 位 Windows 上的内存访问无效

问题描述

我有一个 C DLL 的 JNA 包装器。它工作正常,除非在 Windows 32 位系统上使用。这是一个简化的示例:

int SetData(const wchar_t* data);

int SetId(const wchar_t* id, uint32_t flags);

我按如下方式创建了 JNA 绑定:

public static native int SetData(WString data);

public static native int SetId(WString id, int flags);

第一个函数 SetData() 在 32 位和 64 位 Windows 上都可以正常工作,但第二个函数在 32 位 Windows 7 上崩溃。

我尝试按照其他相关帖子中的建议使用 NativeLong,但没有帮助。

这是存储库的链接:

https://github.com/cryptlex/lexactivator-java/blob/master/src/main/java/com/cryptlex/lexactivator/LexActivatorNative.java

标签: javajava-native-interfacejna

解决方案


您的映射是正确的: WString是正确的映射const wchar_t*,并且int(在 Java 中始终为 32 位)是uint32_t(始终为 32 位)的正确映射,但需要注意的是,当用作标志位掩码时,签名性无关紧要。

我不确定你在哪里读到的NativeLong适合这里。这主要用于 *nix 本机代码,其中sizeof(long)根据操作系统位数而有所不同。实际上,它在 Windows 上实际上并不重要,因为LONG它始终是 32 位的,但它涉及到不必要的对象创建与原语。

JNA 抛出的“无效内存访问”错误是对结构化异常处理捕获的本机内存错误的“优雅”处理。您真正需要知道的是,您正在尝试访问不属于您的本机内存,或者您的本机内存分配失败

调试这些错误总是需要仔细跟踪内存分配。什么时候分配内存?什么时候发布?谁(Java 端或本机端)负责此分配?当您尝试将数据从用户提供的 ID 字符串复制到您的本机 DLL 正在访问的某些本机内存时,您的程序可能会崩溃。所以这指向您需要调查的两个内存指针。

  • 查看正在写入 ID 字符串的内存。找出它的内存何时分配。确保它对于字符串足够大(对于空终止符应该是 2x 字符串长度 + 2 个字节)并正确归零(或显式附加空字节)。验证所有 WinAPI 调用都使用正确的(W 与 A)unicode 版本。

    • 我尝试添加LA_IN_MEMORYflags掩码并收到错误消息“试用尚未开始或已被篡改!试用天数:30”。这显然是由下一行 ( IsLicenseGenuine()) 产生的,这意味着setProductId()调用成功。
    • 在未设置标志时识别您的本机代码的不同之LA_IN_MEMORY处可能会非常有帮助。无效的内存访问可能与识别要使用的目录或文件有关。
      • 最近有一个 3.14.9 的变更日志条目涉及此标志。查看该提交可能会提示问题。
      • 最近 3.15.0 中还有另一个变化,涉及自动检测 Windows 上的文件,这也可能是可疑的,因为这LA_IN_MEMORY会使问题消失。
    • 当给定无效密钥时,错误消息“43:产品 ID 不正确”。被返回,因此在本机代码中访问无主内存的点是在此错误检查之后。
  • 跟踪在 Java 端定义的 ID 字符串发生了什么。给定字符串的常量定义,实际的内存分配可能不是问题,但要跟踪指向该字符串的本机指针,以确保它不会被无意覆盖。

正如您在评论中指出的那样,减少本机内存分配可以解决此问题,表明您正在达到限制。事实证明,堆栈大小 ( ) 的默认 32 位 Java 本机内存分配-Xss是 320 KB。来自Oracle 文档

在 Windows 上,默认线程堆栈大小是从二进制文件 (java.exe) 中读取的。从 Java SE 6 开始,该值在 32 位 VM 中为 320k,在 64 位 VM 中为 1024k。

您可以通过运行 -Xss 选项来减小堆栈大小。例如:

java -server -Xss64k

请注意,在某些版本的 Windows 上,操作系统可能会使用非常粗略的粒度对线程堆栈大小进行四舍五入。如果请求的大小比默认大小小 1K 或更多,则堆栈大小向上舍入到默认值;否则,堆栈大小将向上舍入为 1 MB 的倍数。

您可以增加此限制来解决问题,或者正如您在备注中指出的那样,降低您的本地分配。您可能希望比 300K 更保守,因为这只会留下少量用于堆栈的其他用途。您也可以从较小的值开始,检查返回值ERR_MORE_DATA并使用较大的值重试。300KB 似乎是一个相当大的数量用于注册表值。

Note also that 32-bit Java has a total process memory size limit of either 2GB or 4GB, depending on the OS. If your Java heap allocation grows close to that limit, it reduces the native amount available to you. You can control how big the heap gets with the -Xmx switch and you can also ensure sufficient native memory allocation with the -Xss switch. Use these switches in a combination to avoid hitting the process size limit.


推荐阅读