使用本机 API 的替代解决方案:JVM_LoadClass0、JVM_AllocateNewArray 和 JVM_AllocateNewObject

Posted

技术标签:

【中文标题】使用本机 API 的替代解决方案:JVM_LoadClass0、JVM_AllocateNewArray 和 JVM_AllocateNewObject【英文标题】:Alternative solutions for using native APIs: JVM_LoadClass0, JVM_AllocateNewArray and JVM_AllocateNewObject 【发布时间】:2018-02-06 11:57:46 【问题描述】:

与在 Java 9 中一样,一些原生 API 因弃用而被删除,我没有设法找到替代解决方案来替换它们。我是一名 C++ 开发人员,几乎没有 java 经验。我使用的原生 API 是:JVM_LoadClass0JVM_AllocateNewObjectJVM_AllocateNewArray

我的Java源代码是:

protected Class resolveClass(MercObjectStreamClass v) throws IOException, ClassNotFoundException

    /* Resolve by looking up the stack for a non-zero class
     * loader. If not found use the system loader.
     */
    String name = v.getName();
    try
    
        //we are using the loadClass0 method which calls the native JVM_LoadClass0
        //JVM_LoadClass0 is deprecated and we need to replace the call
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if(loader == null) 
          return loadClass0(null,name);
        

      Class scriptCls =  loader.loadClass(scriptClassname);
        return loadClass0(scriptCls,name);
    
    catch (ClassNotFoundException ex)
    
        Class cl = (Class)primClasses.get(name);
        if (cl != null)
            return cl;
        else
            throw ex;
    

private native Class loadClass0(Class cl, String classname) throws ClassNotFoundException;

然后本机代码只是对 JVM_LoadClass0 的简单调用:

JNIEXPORT jclass JNICALL
Java_mercio_MercObjectInputStream_loadClass0(JNIEnv * env,
    jobject this,
    jclass curClass,
    jstring currClassName)

    return JVM_LoadClass0(env, this, curClass, currClassName);

本机部分与其他 API 类似。

有人可以建议这种方法的替代方法吗?

【问题讨论】:

看起来值得一读:bugs.java.com/bugdatabase/view_bug.do?bug_id=4249833 即使是 Java 6 doesn’t list these functions,所以在不知道这些函数应该做什么的情况下很难提出替代建议。 resolveClass 方法应该做什么?为什么不直接在ClassLoader 上调用loadClass 自我广告,但我想它可以满足您的需求:github.com/nokia/cppJNI 您在 jvm.h 中找到的 JVM_* 函数不是受支持/记录的接口,因此它们可以随时更改。最好坚持使用 JNI 接口,您可以在其中找到 FindClass 和 AllocObect 等函数。 【参考方案1】:

我查了JVM_LoadClass0的源码,发现如下评论

// Load a class relative to the most recent class on the stack  with a non-null
// classloader.
// This function has been deprecated and should not be considered part of the
// specified JVM interface.

附加currClass参数的目的只能从实际代码中猜测,但显然“堆栈上最近的类”只有在currClass不是null时才使用,否则@987654326的加载器使用@。

这引发了一些关于resolveClass 方法意图的问题。

1234563班级。使用您自己的类加载器来解析一个类非常简单,您只需调用Class.forName(name)

您正在探测Thread.currentThread().getContextClassLoader(),默认为ClassLoader.getSystemClassLoader(),很少设置为null。因此,在堆栈上探测类的行为似乎是一种罕见的极端情况。

当上下文加载器不是null时,你不是用它来加载类,而是做loader.loadClass(scriptClassname); return loadClass0(scriptCls,name);,首先加载一个scriptClassname,然后使用结果的加载器来解析name。如果这是故意的,您可以使用Class<?> scriptCls = loader.loadClass(scriptClassname); return Class.forName(name, true, scriptCls.getClassLoader()); 来实现相同的目的,而无需使用本机方法。

您可以在不使用本机代码的情况下使用与 Java 9 中的原始代码完全相同的操作

protected Class resolveClass(MercObjectStreamClass v)
                                       throws IOException, ClassNotFoundException

    String name = v.getName();
    try
    
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if(loader != null)
            loader = loader.loadClass(scriptClassname).getClassLoader();
        else
            loader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
                        .walk(s -> s.map(sf -> sf.getDeclaringClass().getClassLoader())
                                    .filter(Objects::nonNull)
                                    .findFirst().orElse(null));

        return Class.forName(name, true, loader);
    
    catch (ClassNotFoundException ex)
    
        Class cl = (Class)primClasses.get(name);
        if (cl != null)
            return cl;
        else
            throw ex;
    

但是,如前所述,您应该首先重新考虑该方法实际上应该做什么。

【讨论】:

嗨,这可能是一个可以用 java 6 编译的代码,因为它应该从 6 开始使用任何类型的 java 运行?谢谢。 @istik 很遗憾不是,StackWalker 是 Java 9 的功能,并且没有(标准)Java 6 功能能够执行调用者敏感操作。但正如答案中所解释的,您应该重新考虑是否真的需要这种调用者敏感性,因为它似乎无论如何都不适用于实际场景。 你是对的,我从来没有设法获得它到达那个分支的场景。非常感谢你的帮忙。我还设法通过AllocObject JNI 调用解决了VM_AllocateNewObject 问题。我唯一剩下的就是JVM_AllocateNewArray。我仍然无法获得与早期 Java 版本相同的结果。 在调用本机方法之前有一条评论说:/* This can't be done with new because only the top level array * is needed and the type must be set properly. * the lower level arrays will be created when they are read. */ 我想知道是否应该为此打开一个新主题。 如果你写new Type[size][][],只会创建“***数组”,而new Type[size1][size2][size3]会创建一堆子数组。据我了解,JNI_NewObjectArray 也只创建了“***数组”(因为它甚至没有子维度的参数)。【参考方案2】:

This doc page at oracle.com 包含 Java 9 中可用的 JNI 函数的完整列表。

通过查看您提到的旧 JNI 函数,我猜您可能对以下内容特别感兴趣:

class operations (DefineClass(), FindClass(), ...) object operations (AllocObject(), NewObject(), ...) array operations (NewObjectArray(), GetArrayLength(), ...)

【讨论】:

以上是关于使用本机 API 的替代解决方案:JVM_LoadClass0、JVM_AllocateNewArray 和 JVM_AllocateNewObject的主要内容,如果未能解决你的问题,请参考以下文章

JPQL 中带有 JPA 的本机查询的“GROUP_CONCAT”是不是有替代方案?

11 岁以下 api 的 setTop 替代方案

API 级别 19 或更低级别的 LocalDate 替代方案?

已弃用的 google plus api 的替代解决方案是啥?

什么是 Google Places API 的一个好的、无限的替代方案? [关闭]

Android 5.0 (API 21) 中 AudioManager.setRingerMode() 的替代方案