我有一个在Tomcat 3.2.1下运行的webapp,需要进行JNI调用才能访问旧C++代码中的数据和方法.在启动webapp时加载servlet,作为其init方法的一部分,将使特定于该webapp实例的数据集加载到C++数据结构中.
此servlet的此Java代码包含以下内容:
static
{
try {
System.loadLibrary("JCoreImpl");
System.out.println("JCoreImpl loaded");
m_bLibraryLoaded = true;
} catch (UnsatisfiedLinkError e) {
m_bLibraryLoaded = false;
System.out.println("JCoreImpl NOT loaded " + e);
}
}
Run Code Online (Sandbox Code Playgroud)
如果只有一个webapp(我们称之为"webapps/aaa"),事情就可以了.
如果我有第二个webapp("webapps/bbb")与webapps/aaa相同,除了C++数据结构中使用的数据集,那么webapps/aaa启动就好了,但是当webapps/bbb启动时,我得到了错误陈述:
JCoreImpl NOT loaded java.lang.UnsatisfiedLinkError: Native Library
E:\WebStation\binDebug\JCoreImpl.dll already loaded in another classloader
Run Code Online (Sandbox Code Playgroud)
我需要为每个webapps都有一个单独的本机库实例,因为每个实例都需要包含该特定webapp所特有的数据.我搜索了邮件档案并阅读Craig McLanahan的电子邮件,解释了类加载器的层次结构.但是我无法找到任何特定于为每个webapp加载本机库的唯一实例的内容.
我一直试图在Windows上与Boost和android结婚很长时间并尝试了很多方法,但仍然没有运气.我想在android中使用Boost库制作一个示例程序.我在这里关注本教程.
正如本教程建议我将我的Boost lib保存在****(Android NDK)\ sources\boost_1_44_0****中成功编译.
然后我在sources/boost_1_44_0中创建了一个Android.mk文件,并输入了我想要使用的每个库的条目.在这种情况下,lib.文件是libboost_date_time-gcc-mt-s-1_44.a在boost_1_44_0/android/lib /中可用.
这是Android.mk文件的内容.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= boost_date
LOCAL_SRC_FILES:= boost_1_44_0/android/lib/libboost_date_time-gcc-mt-s-1_44.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
include $(PREBUILT_STATIC_LIBRARY)
Run Code Online (Sandbox Code Playgroud)
现在,下一步是在我的项目目录中,在jni文件夹中创建一个Android.mk文件.(这是为了创建一个共享库.).这是它的内容.
LOCAL_PATH := $(call my-dir)
include $(call all-subdir-makefiles)
include $(CLEAR_VARS)
# Here we give our module name and source file(s)
LOCAL_LDLIBS := -llog -ldl
LOCAL_MODULE := ndkfoo
LOCAL_SRC_FILES := ndkfoo.cpp
LOCAL_STATIC_LIBRARIES := boost_date
include $(BUILD_SHARED_LIBRARY)
$(call import-module,boost_1_44_0)
Run Code Online (Sandbox Code Playgroud)
这是放在jni文件夹内的同一位置的Application.mk文件.Application.mk文件的内容如下:
APP_STL …Run Code Online (Sandbox Code Playgroud) 我正在使用SWIG制作一个C++库的Java包装器(关于Json(de)序列化)以在Android上使用它.我在C++中定义了一个抽象类,表示可以(反)序列化的对象:
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};Run Code Online (Sandbox Code Playgroud)
现在,我正在尝试从这个类生成一个Java接口.这是我的SWIG界面:
%module JsonSerializable
%{
#include "JsonSerializable.hpp"
%}
%import "JsonValue.i"
class IJsonSerializable {
public:
virtual void serialize(Value &root) = 0;
virtual void deserialize(Value &root) = 0;
};Run Code Online (Sandbox Code Playgroud)
但是生成的Java代码(显然,因为我无法找到如何告诉SWIG这是一个接口)一个简单的类,有两个方法和一个默认的构造函数/析构函数:
public class IJsonSerializable {
private long swigCPtr;
protected boolean swigCMemOwn;
public IJsonSerializable(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
public static long getCPtr(IJsonSerializable obj) {
return (obj == null) ? 0 …Run Code Online (Sandbox Code Playgroud) 我在IBM上读到了这一点
要访问Java对象的字段并调用其方法,本机代码必须调用FindClass(),GetFieldID(),GetMethodId()和GetStaticMethodID().对于GetFieldID(),GetMethodID()和GetStaticMethodID(),为给定类返回的ID在JVM进程的生命周期内不会更改.但是获取字段或方法的调用可能需要在JVM中进行大量工作,因为字段和方法可能已经从超类继承,使得JVM在类层次结构中向上移动以找到它们.因为给定类的ID是相同的,所以您应该查找它们一次,然后重复使用它们.同样,查找类对象可能很昂贵,因此它们也应该被缓存.
一个人如何缓存的methodID,fieldID以及class在JNI对象?是否有必须遵循的内置方法或特定程序?
这是JNI代码.
Java代码:
public class Sample1 {
public native String stringMethod(String text);
public static void main(String[] args)
{
System.loadLibrary("Sample1");
Sample1 sample = new Sample1();
String text = sample.stringMethod("world");
System.out.println("stringMethod: " + text);
}
}
Run Code Online (Sandbox Code Playgroud)
stringMethod函数的Cpp方法:
JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
(JNIEnv *env, jobject obj, jstring string) {
const char *name = env->GetStringUTFChars(string, NULL);//Java String to C Style string
char msg[60] = "Hello ";
jstring result;
strcat(msg, name);
env->ReleaseStringUTFChars(string, name);
puts(msg);
result = env->NewStringUTF(msg); // C style string to Java String
return result; …Run Code Online (Sandbox Code Playgroud) 我在C++的GCC文档中发现了一个有趣的特性:
java_interface
此类型属性通知C++该类是Java接口.它可能只适用于在extern"Java"块中声明的类.将使用GCJ的接口表机制调度对此接口中声明的方法的调用,而不是常规的虚拟表调度.
据我所知,它看起来像这样:
extern "Java" {
class NativeClass __attribute__((java_interface)) {
//Implementation on native methods goes here.
}
}
Run Code Online (Sandbox Code Playgroud)
有人知道这方面的细节吗?如何从Java 调用NativeClass的方法?也许有人在现场试过吗?
我目前正在尝试诊断应用程序中的缓慢内存泄漏.我到目前为止的事实如下.
尽管只是被WeakReferences引用(根据Eclipse Memory Analyzer Tool),但是可能导致这些Foo类无法收集的原因是什么?
EDIT1:
@mindas我使用的WeakReference等同于以下示例代码.
public class FooWeakRef extends WeakReference<Foo>
{
public long longA;
public long longB;
public String stringA;
public FooWeakRef(Foo xiObject, ReferenceQueue<Foo> xiQueue)
{
super(xiObject, xiQueue);
}
}
Run Code Online (Sandbox Code Playgroud)
Foo没有终结器,只要没有清除WeakRefs,任何终结器都不会被考虑.当一个对象弱可达时,它不能最终确定.有关详情,请参阅此页面.
@kasten在对象可最终化之前清除弱引用.我的堆转储显示这没有发生.
@jarnbjo我引用WeakReference Javadoc:
"假设垃圾收集器在某个时间点确定一个对象是弱可达的.那时它将原子地清除对该对象的所有弱引用以及对该对象可从其访问的任何其他弱可达对象的所有弱引用通过一系列强大而柔软的参考资料."
这告诉我,GC应该检测到我的Foo对象是"弱可达"和"当时"清除弱引用这一事实.
编辑2
@j flemm - 我知道40mb听起来并不多,但我担心4天内40mb意味着100天内4000mb.我读过的所有文档都表明,弱可达的对象不应该闲置几天.因此,我对如何在没有引用显示在堆转储中时强烈引用对象的任何其他解释感兴趣.
当一些悬空Foo对象存在时,我将尝试分配一些大对象,并查看JVM是否收集它们.但是,此测试需要几天时间才能完成设置.
编辑3
@jarnbjo - 我知道我不能保证JDK何时会注意到一个对象是弱可达的.但是,我认为重载4天的应用程序会为GC提供足够的机会来注意我的对象是弱可达的.4天后,我强烈怀疑其余的弱引用对象已经以某种方式泄露.
编辑4
@j flemm - 多数民众赞成真有意思!只是为了澄清,你是说GC正在你的应用程序上发生并且没有清除Soft/Weak refs?您能否告诉我有关您正在使用的JVM + GC配置的更多详细信息?我的应用程序使用80%堆的内存条来触发GC.我假设任何旧的GC的GC都会清除Weak refs.一旦内存使用率高于更高的阈值,您是否建议GC仅收集弱引用?这个更高的限制是否可配置?
编辑5
@j flemm - 关于在SoftRefs之前清除WeakRefs的评论与Javadoc一致,其中声明:SoftRef:"假设垃圾收集器在某个时间点确定一个对象可以轻柔地到达.那时它可以选择清除原子地对该对象的所有软引用以及对任何其他可通过一系列强引用访问该对象的软可引用对象的所有软引用.同时或稍后它会将那些新清除的软引用排入队列在参考队列中注册."
WeakRef:"假设垃圾收集器在某个时间点确定一个对象是弱可达的.那时它将原子地清除对该对象的所有弱引用以及对该对象的任何其他弱可达对象的所有弱引用可以通过一系列强引用和软引用来访问.同时它将声明所有以前弱可达的对象都可以最终确定.同时或稍后它会将那些新清除的弱引用排入队列.在参考队列中注册."
为清楚起见,您是说当您的应用程序具有超过50%的可用内存时垃圾收集器运行,并且在这种情况下它不会清除WeakRefs?当你的应用程序有超过50%的可用内存时,为什么GC会运行?我认为你的应用程序可能只是产生非常少量的垃圾,当收集器运行时它正在清除WeakRefs而不是SoftRef.
编辑6 …
我按如下方式调用ndk-stack:
cat file_temp | ~/workspace/android-ndk-r6b/ndk-stack -sym /home/xyz/trunk/apk/obj/local/armeabi/
Run Code Online (Sandbox Code Playgroud)
它找到了崩溃指纹,但未能显示堆栈分析.打印错误是:
ndk-stack: elff/elf_file.cc:102: static ElfFile* ElfFile::Create(const char*): Assertion `read_bytes != -1 && read_bytes == sizeof(header)' failed. Stack frame #00 pc 43121300 Aborted
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
谢谢.
我用Google搜索并获得了一些答案,可以通过GCJNI(现在网站已关闭)和LambdaVM完成Java和Haskell之间的通信.要使用LambdaVM/GCJNI,我是否需要下载任何构建工具?我在哪里可以了解更多关于它们的信息,因为我在网上找不到太多资源?
我想开发一个在Java和Haskell之间进行通信的应用程序(我将从Java获取输入将其传递给Haskell并在那里处理并将结果返回给Java).这就是我想要做的.请帮我...
在将NDK版本升级到Android Studio中的最新版本后,我上次遇到此问题.我也找到了解决这个问题的方法.如果有人有这个问题,我希望这是你最好的问题和答案.请检查我的答案.
java ×5
android ×3
c++ ×3
android-ndk ×2
boost ×1
caching ×1
gcc ×1
gcj ×1
haskell ×1
javabeans ×1
memory-leaks ×1
performance ×1
return ×1
string ×1
swig ×1