使用本机 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_LoadClass0
、JVM_AllocateNewObject
和 JVM_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
方法意图的问题。
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”是不是有替代方案?
API 级别 19 或更低级别的 LocalDate 替代方案?
已弃用的 google plus api 的替代解决方案是啥?