JNI Java对象实例化期间的Segfault
Segfault during JNI Java object instantiation
(我知道这个问题。这是一个单独的问题。)
在从C++JNI代码实例化Java对象时,我遇到了一个segfault。
以下是分段故障:
# SIGSEGV (0xb) at pc=0x00007f2c7ba13548, pid=2809, tid=139829052933888
#
# JRE version: Java(TM) SE Runtime Environment (8.0_45-b14) (build 1.8.0_45-b14)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.45-b02 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x559548] Dictionary::add_protection_domain(int, unsigned int, instanceKlassHandle, ClassLoaderData*, Handle, Thread*)+0x128
以下是hs_err文件中堆栈的相关部分:
Stack: [0x00007f2c7cf78000,0x00007f2c7d079000], sp=0x00007f2c7d0760c0, free space=1016k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x559548] Dictionary::add_protection_domain(int, unsigned int, instanceKlassHandle, ClassLoaderData*, Handle, Thread*)+0x128
V [libjvm.so+0xa1f4e2] SystemDictionary::validate_protection_domain(instanceKlassHandle, Handle, Handle, Thread*)+0x142
V [libjvm.so+0xa230d4] SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, Thread*)+0x3c4
V [libjvm.so+0xa24a13] SystemDictionary::resolve_or_fail(Symbol*, Handle, Handle, bool, Thread*)+0x33
V [libjvm.so+0x4ea4f8] ConstantPool::klass_at_impl(constantPoolHandle, int, Thread*)+0x158
V [libjvm.so+0x4efbd0] ConstantPool::klass_ref_at(int, Thread*)+0xa0
V [libjvm.so+0x7e0eb1] LinkResolver::resolve_pool(KlassHandle&, Symbol*&, Symbol*&, KlassHandle&, constantPoolHandle, int, Thread*)+0x71
V [libjvm.so+0x7e7206] LinkResolver::resolve_invokeinterface(CallInfo&, Handle, constantPoolHandle, int, Thread*)+0xa6
V [libjvm.so+0x7e9ad0] LinkResolver::resolve_invoke(CallInfo&, Handle, constantPoolHandle, int, Bytecodes::Code, Thread*)+0xb0
V [libjvm.so+0x67a972] InterpreterRuntime::resolve_invoke(JavaThread*, Bytecodes::Code)+0x1b2
j [my-package].NativeRef$X509_ATTRIBUTE.<init>(J)V+21
v ~StubRoutines::call_stub
V [libjvm.so+0x681a26] JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x1056
V [libjvm.so+0x6c432b] jni_invoke_nonstatic(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*)+0x41b
V [libjvm.so+0x6d943b] jni_NewObjectV+0x2ab
C [[my-so].so+0x2b855] JNIEnv_::NewObject(_jclass*, _jmethodID*, ...)+0xb5
C [[my-so].so+0x7d31e] _jobject* newNativeRef<UniquePtr<x509_attributes_st, X509_ATTRIBUTE_Delete> >(JNIEnv_*, char const*, UniquePtr<x509_attributes_st, X509_ATTRIBUTE_Delete>*, bool)+0x352
快速概述我的库:我的代码环绕OpenSSL,通过JCA API为Java应用程序提供对OpenSSL的访问。(注意:并非所有代码都是从头开始编写的。一些代码源自Conscrypt,这是一个执行类似功能的Android库。)
我有许多几乎相同的类(本地指针的简单Java包装器,它自动从finalize()方法中释放本地对象),它们都扩展了一个名为NativeRef的类。(部分代码如下。)
因此,我创建了一个通用的本机函数(newNativeRef())来获取本机指针并实例化Java对象,将本机指针传递给构造函数。该功能已经使用了一年多,并且经过了大量测试。到目前为止,我没有遇到任何问题。(部分代码如下)
但我现在正在编写一个新的JNI函数,它调用newNativeRef()并给出segfault。
以下是我目前正在编写的新JNI函数中的调用:
ret = newNativeRef<Unique_X509_ATTRIBUTE>(env, CLASS_NR_X509_ATTRIBUTE, &attr, false);
attr是一个简单的智能指针(Unique_X509_ATTRIBUTE),它围绕着指向OpenSSL X509_ATTPRIBUTE对象的基本指针。
CLASS_NR_X509_ATTRIBUTE定义为"[my package]/NativeRef$X509_ATTPRIBUTE">
以下是Unique_X509_ATTRIBUTE:的定义
struct X509_ATTRIBUTE_Delete {
void operator()(X509_ATTRIBUTE* p) const {
X509_ATTRIBUTE_free(p);
}
};
typedef UniquePtr<X509_ATTRIBUTE, X509_ATTRIBUTE_Delete> Unique_X509_ATTRIBUTE;
以下是UniquePtr 的相关(精简)代码
template <typename T, typename D = DefaultDelete<T> >
class UniquePtr {
public:
// Construct a new UniquePtr, taking ownership of the given raw pointer.
explicit UniquePtr(T* ptr = (T*) NULL) : mPtr(ptr) {
}
T* get() const { return mPtr; }
// Returns the raw pointer and hands over ownership to the caller.
// The pointer will not be deleted by UniquePtr.
T* release() CJ_ATTRIBUTE(((warn_unused_result))){
T* result = mPtr;
mPtr = NULL;
return result;
}
// Takes ownership of the given raw pointer.
// If this smart pointer previously owned a different raw pointer, that
// raw pointer will be freed.
void reset(T* ptr = (T*) NULL) {
if (ptr != mPtr) {
D()(mPtr); // Basically, this calls X509_ATTRIBUTE_free(mPtr)
mPtr = ptr;
}
}
private:
// The raw pointer.
T* mPtr;
};
以下是发生segfault的函数:
template<typename T> jobject newNativeRef(JNIEnv* env, const char* className,
T* addr) {
jclass clazz;
jobject object = JNIHandle_NULL;
try {
clazz = env->FindClass(className);
[snip - error-checking]
jmethodID constructor = env->GetMethodID(clazz, "<init>",
NATIVE_FUNC_SIG(SIG_VOID, SIG_LONG));
jlong jaddr = JNIHandle_NULL;
if (addr != NULL) {
jaddr =
static_cast<jlong>(reinterpret_cast<uintptr_t>((*addr).get()));
}
// This line generates the segfault.
object = env->NewObject(clazz, constructor, jaddr);
if (addr != NULL) {
// Ownership of the pointer has been passed to the Java object
OWNERSHIP_TRANSFERRED((*addr));
}
[snip - error handling]
return object;
}
NATIVE_UNC_SIG(SIG_VOID,SIG_LONG)计算为"(J)V">
以下是用于包装指向X509_ATTRIBUTE对象的指针的Java类(即我们试图创建其实例的类):
public static class X509_ATTRIBUTE extends NativeRef {
public X509_ATTRIBUTE(long ctx) {
super(ctx);
}
protected void opensslFree() {
[Native-function-class].X509_ATTRIBUTE_free(this);
}
}
和NativeRef超级类(精简版):
public abstract class NativeRef {
transient volatile long context = 0L;
public NativeRef(long ctx) {
this.context = ctx;
}
@Override
protected synchronized void finalize() throws Throwable {
try {
free();
} finally {
super.finalize();
}
}
public synchronized void free() {
try {
if (isValid()) {
opensslFree();
}
} finally {
context = 0L;
}
}
protected abstract void opensslFree();
}
提前感谢!
[编辑]
有趣的是,崩溃似乎并不是这个newNativeRef()调用所特有的。在注释掉newNativeRef()调用之后,下次JNI尝试实例化Java对象时(在本例中,是Java异常),会出现相同的segfault。
我甚至在stacktraces中看到了同样的segfault,它不包括我的任何JNI代码。以下是一次此类崩溃的hs_err堆栈争用:
Stack: [0x00007f8535bf7000,0x00007f8535cf8000], sp=0x00007f8535cf5bf0, free space=1018k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V [libjvm.so+0x559548] Dictionary::add_protection_domain(int, unsigned int, instanceKlassHandle, ClassLoaderData*, Handle, Thread*)+0x128
V [libjvm.so+0xa1f4e2] SystemDictionary::validate_protection_domain(instanceKlassHandle, Handle, Handle, Thread*)+0x142
V [libjvm.so+0xa230d4] SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, Thread*)+0x3c4
V [libjvm.so+0xa24a13] SystemDictionary::resolve_or_fail(Symbol*, Handle, Handle, bool, Thread*)+0x33
V [libjvm.so+0x4ea4f8] ConstantPool::klass_at_impl(constantPoolHandle, int, Thread*)+0x158
V [libjvm.so+0x89d141] Method::fast_exception_handler_bci_for(methodHandle, KlassHandle, int, Thread*)+0x141
V [libjvm.so+0x6774dd] InterpreterRuntime::exception_handler_for_exception(JavaThread*, oopDesc*)+0x32d
j CertRequest.setAttribute(Ljava/lang/String;Ljava/lang/String;)V+32
j MyApp.run()V+176
j MyApp.main([Ljava/lang/String;)V+7
v ~StubRoutines::call_stub
V [libjvm.so+0x681a26] JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x1056
V [libjvm.so+0x6c3692] jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, Thread*)+0x362
V [libjvm.so+0x6e009a] jni_CallStaticVoidMethod+0x17a
C [libjli.so+0x7bcc] JavaMain+0x80c
C [libpthread.so.0+0x7dc5] start_thread+0xc5
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j CertRequest.setAttribute(Ljava/lang/String;Ljava/lang/String;)V+32
j MyApp.run()V+176
j MyApp.main([Ljava/lang/String;)V+7
v ~StubRoutines::call_stub
发现早期的函数损坏了一些内存,这将在下一次从JNI调用Java时导致半随机错误。
- 从C++实例化QML
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 约束和显式模板实例化
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 对象实例化调用构造函数的次数太多
- 如何使用非默认构造函数实例化模板化类
- 静态数据成员模板专用化的实例化点在哪里
- 错误的cv::face FacemarkLBF实例化
- C++的解析器在可以区分比较和模板实例化之前会做什么?
- 为什么 gcc 和 clang 为函数模板的实例化生成不同的符号名称?
- 检查某些类型是否是模板类 std::optional 的实例化
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 无法使用 SWIG 在 Python 中实例化C++类(获取属性错误)
- 模板化类构造函数的模板实例化
- 在 c++ 中的模板实例化中使用带有构造函数的类作为类型参数
- 受约束的成员函数和显式模板实例化
- JNI Java对象实例化期间的Segfault
- OpenCV-segfault实例化冲浪特征检测器