实现的JNI接口,同一套代码,为什么在windows下性能比JAVA实现要好很多。但在linux下实现性能却比JAVA实现差很多。以下是具体数据:
在Windows 11 下(4核8线程 Intel(R) Core(TM)i7-10510U CPU @ 1.80GHz)
项目是navmesh 3D寻路,
在Windows 11 下(64位,4核8线程 Intel(R) Core(TM)i7-10510U CPU @ 1.80GHz)
Benchmark Mode Cnt Score Error Units
NavEngineBenchMark.javaFind thrpt 10 28605.626 ± 1331.775 ops/s
NavEngineBenchMark.javaFindNearest thrpt 10 497177.122 ± 12048.418 ops/s
NavEngineBenchMark.javaRaycast thrpt 10 401200.127 ± 7926.908 ops/s
NavEngineBenchMark.nativeFind thrpt 10 82924.746 ± 1942.112 ops/s
NavEngineBenchMark.nativeFindNearest thrpt 10 1438972.883 ± 28769.610 ops/s
NavEngineBenchMark.nativeRaycast thrpt 10 1096445.690 ± 29597.755 ops/s
在CentOS Linux 7 (64位,8核16线程 Intel(R) Xeon(R) Platinum 8372C CPU model 106 @ 3.20GHz)
Benchmark Mode Cnt Score Error Units
NavEngineBenchMark.javaFind thrpt 10 38372.243 ± 80.293 ops/s
NavEngineBenchMark.javaFindNearest thrpt 10 518859.216 ± 767.435 ops/s
NavEngineBenchMark.javaRaycast thrpt 10 442360.257 ± 287.334 ops/s
NavEngineBenchMark.nativeFind thrpt 10 26796.756 ± 49.669 ops/s
NavEngineBenchMark.nativeFindNearest thrpt 10 393484.307 ± 844.553 ops/s
NavEngineBenchMark.nativeRaycast thrpt 10 305434.422 ± 851.248 ops/s
jmh 性能对比项目地址为:https://github.com/jiangguilong2000/gamioo/
jni 项目为: 为https://github.com/jiangguilong2000/recastnavigation
主要代码维护在RecastDll里的NavEngine.cpp,对应到JAVA的是io.gamioo.nav.NavEngine.
项目是navmesh 3D寻路,在IDEA下,运行NavEngineBenchMark直接跑起来,或者在windows下和linux下到gamioo目录,执行gradle gamioo-navigation:jmh,能直接跑出如上结果
由于有部分小伙伴提到需要给出源码,才能给出建议,现给出如下:
主要5个文件,
io_gamioo_nav_NavEngine.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class io_gamioo_nav_NavEngine */
#ifndef _Included_io_gamioo_nav_NavEngine
#define _Included_io_gamioo_nav_NavEngine
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: io_gamioo_nav_NavEngine
* Method: load
* Signature: (I[BI)I
*/
JNIEXPORT jint JNICALL Java_io_gamioo_nav_NavEngine_load
(JNIEnv *, jobject, jint, jbyteArray, jint);
/*
* Class: io_gamioo_nav_NavEngine
* Method: find
* Signature: (IFFFFFF)[F
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_find
(JNIEnv *, jobject, jint, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat);
/*
* Class: io_gamioo_nav_NavEngine
* Method: findNearest
* Signature: (IFFF)[F
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_findNearest
(JNIEnv *, jobject, jint, jfloat, jfloat, jfloat);
/*
* Class: io_gamioo_nav_NavEngine
* Method: raycast
* Signature: (IFFFFFF)[F
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_raycast
(JNIEnv *, jobject, jint, jfloat, jfloat, jfloat, jfloat, jfloat, jfloat);
/*
* Class: io_gamioo_nav_NavEngine
* Method: release
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_release
(JNIEnv *, jobject, jint);
/*
* Class: io_gamioo_nav_NavEngine
* Method: releaseAll
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_releaseAll
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
NavEngine.h
#include <cstring>
#include <unordered_map>
#include <iostream>
#include <exception>
#include <string>
#include <cstdint>
#include <map>
#include <sstream>
class NavMeshContex;
class NavMesh
{
public:
static NavMesh* instance;
static NavMesh* GetInstace();
std::map<int32_t, NavMeshContex*> navMeshContexs;
NavMeshContex* New(int32_t id, const char* buffer, int32_t n);
NavMeshContex* Get(int32_t id);
void Clear();
const char* Version();
void Remove(int32_t id);
private:
NavMesh();
};
struct NavMeshSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams params;
};
struct NavMeshTileHeader
{
dtTileRef tileRef;
int dataSize;
};
Util.h
#pragma once
namespace Util
{
/**JByteaArray -> char* */
static char* ConvertJByteaArrayToChars(JNIEnv* env, jbyteArray bytearray){
char* chars = NULL;
jbyte* bytes;
bytes = env->GetByteArrayElements(bytearray, 0);
size_t chars_len = env->GetArrayLength(bytearray);
chars = new char[chars_len + 1];
memset(chars, 0, chars_len + 1);
memcpy(chars, bytes, chars_len);
chars[chars_len] = 0;
env->ReleaseByteArrayElements(bytearray, bytes, 0);
return chars;
}
/** float* -> jfloatArray */
static jfloatArray ConvertFloatStarToJfloatArray(JNIEnv* env, float* array, int length) {
jfloatArray ret = env->NewFloatArray(length);
env->SetFloatArrayRegion(ret, 0, length, array);
return ret;
}
};
核心代码: NavEngine.cpp
#include "DetourNavMesh.h"
#include "jni.h"
#include "DetourNavMeshQuery.h"
#include <cstring>
#include <unordered_map>
#include "DetourCommon.h"
#include <iostream>
#include <exception>
#include <string>
#include <cstdint>
#include <map>
#include "io_gamioo_nav_NavEngine.h"
#include "Util.h"
#include "NavEngine.h"
#include <sstream>
using namespace std;
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
static const float extents[3] = {1.0f,1.0f,1.0f};
static const float raycastExtents[3] = { 0.0f,1.0f,0.0f };
int32_t InitNav(const char* buffer, int32_t n, dtNavMesh*& navMesh)
{
int index = 0;
// Read header.
NavMeshSetHeader header;
int count = sizeof(NavMeshSetHeader);
if (index + count > n)
{
return -1;
}
memcpy(&header, buffer + index, count);
index += count;
if (header.magic != NAVMESHSET_MAGIC)
{
return -2;
}
if (header.version != NAVMESHSET_VERSION)
{
return -3;
}
dtNavMesh* mesh = dtAllocNavMesh();
if (!mesh)
{
return -4;
}
dtStatus status = mesh->init(&header.params);
if (dtStatusFailed(status))
{
return -5;
}
// Read tiles.
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
count = sizeof(NavMeshTileHeader);
if (index + count > n)
{
return -6;
}
memcpy(&tileHeader, buffer + index, count);
index += count;
if (!tileHeader.tileRef || !tileHeader.dataSize)
break;
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
if (!data) break;
memset(data, 0, tileHeader.dataSize);
count = tileHeader.dataSize;
if (index + count > n)
{
return -7;
}
memcpy(data, buffer + index, count);
index += count;
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
}
navMesh = mesh;
return 0;
}
static const int MAX_POLYS = 256;
static const int MAX_SMOOTH = 2048;
class NavMeshContex
{
public:
dtNavMesh* navMesh;
dtNavMeshQuery* navQuery;
int32_t Init(const char* buffer, int32_t n)
{
int32_t ret = InitNav(buffer, n, navMesh);
std::string s;
if (ret != 0)
{
return -1;
}
navQuery = new dtNavMeshQuery();
navQuery->init(navMesh, 2048);
return 0;
}
~NavMeshContex()
{
if (navQuery != nullptr)
{
dtFreeNavMeshQuery(navQuery);
}
if (navMesh != nullptr)
{
dtFreeNavMesh(navMesh);
}
}
std::string toString() {
std::stringstream ss;
ss << "NavMeshContex[" << static_cast<const void*>(this) << "]";
return ss.str();
}
};
NavMesh* NavMesh::instance = nullptr;
NavMesh::NavMesh()
{
}
NavMesh* NavMesh::GetInstace()
{
if (NavMesh::instance == nullptr)
{
NavMesh::instance = new NavMesh();
}
return NavMesh::instance;
}
NavMeshContex* NavMesh::New(int32_t id, const char* buffer, int32_t n)
{
NavMeshContex* navMeshContex = new NavMeshContex();
int32_t ret = navMeshContex->Init(buffer, n);
if (ret != 0)
{
delete navMeshContex;
return nullptr;
}
navMeshContexs[id] = navMeshContex;
return navMeshContex;
}
NavMeshContex* NavMesh::Get(int32_t id)
{
const auto it = navMeshContexs.find(id);
if (it != navMeshContexs.end())
{
return it->second;
}
return nullptr;
}
void NavMesh::Clear()
{
for (auto kv : navMeshContexs)
{
delete kv.second;
}
navMeshContexs.clear();
}
void NavMesh::Remove(int32_t id)
{
const auto it = navMeshContexs.find(id);
if (it != navMeshContexs.end())
{
NavMeshContex* navMesh = it->second;
navMeshContexs.erase(it);
delete navMesh;
}
}
const char* NavMesh::Version()
{
return ""+ NAVMESHSET_VERSION;
}
/**
* 加载地图
*
* @param id 寻路数据地图ID
* @param content 地图文件的路径例
* @param length 数据长度
* @return navmeshId, 为0或负数表示加载失败,为正数表示加载成功,后续寻路时传入此id为参数
*/
JNIEXPORT jint JNICALL Java_io_gamioo_nav_NavEngine_load(JNIEnv* env, jobject jobj, jint id, jbyteArray content, jint length)
{
char* buffer = Util::ConvertJByteaArrayToChars(env, content);
NavMeshContex* context=NavMesh::GetInstace()->New(id, buffer, length);
delete buffer;
if (context== nullptr) {
return -1;
}
else {
return id;
}
}
NavMeshContex* RecastGet(int32_t id)
{
return NavMesh::GetInstace()->Get(id);
}
/**
* 寻路
*
* @param navmeshId 寻路数据地图ID
* @param startX 起始点X
* @param startY 起始点Y
* @param endX 结束点X
* @param endY 结束点Y
* @return 返回路径点列表,注意,查找不到时,会返回空
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_find(JNIEnv* env, jobject jobj, jint id, jfloat startX, jfloat startY, jfloat startZ, jfloat endX, jfloat endY, jfloat endZ)
{
jfloatArray ret= NULL;
float straightPath[MAX_POLYS * 3];
float startPos[3] = {startX ,startY,startZ };
float endPos[3] = {endX ,endY,endZ };
dtPolyRef startRef = 0;
dtPolyRef endRef = 0;
float startNearestPt[3];
float endNearestPt[3];
dtQueryFilter filter;
filter.setIncludeFlags(0xffff);
filter.setExcludeFlags(0);
NavMeshContex* navMeshContex =RecastGet(id);
if (navMeshContex==nullptr)
{
return ret;
}
//cout << navMeshContex->toString()<<endl;
int dtStatus =navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, startNearestPt);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
dtStatus =navMeshContex->navQuery->findNearestPoly(endPos, extents, &filter, &endRef, endNearestPt);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
dtPolyRef polys[MAX_POLYS];
int npolys;
unsigned char straightPathFlags[MAX_POLYS];
dtPolyRef straightPathPolys[MAX_POLYS];
int nstraightPath = 0;
dtStatus=navMeshContex->navQuery->findPath(startRef, endRef, startNearestPt, endNearestPt, &filter, polys, &npolys, MAX_POLYS);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
//printf("npolys: %d\n", npolys);
if (npolys)
{
float epos1[3];
dtVcopy(epos1, endNearestPt);
if (polys[npolys - 1] != endRef)
{
dtStatus=navMeshContex->navQuery->closestPointOnPoly(polys[npolys - 1], endNearestPt, epos1, 0);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
}
dtStatus = navMeshContex->navQuery-> findStraightPath(startNearestPt, endNearestPt, polys, npolys, straightPath, straightPathFlags, straightPathPolys, &nstraightPath, MAX_POLYS, 0);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
ret= Util::ConvertFloatStarToJfloatArray(env, straightPath,nstraightPath * 3);
}
return ret;
}
/**
* 光线照射发,寻找可以支线通过的hit点,如果可通过则返回hit
*
* @param navmeshId 寻路数据地图ID
* @param startX 起始点X
* @param startY 起始点Y
* @param endX 结束点X
* @param endY 结束点Y
* @return 返回射线射过去遇到的第一个阻挡点,如果到终点都没有阻挡,返回终点
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_raycast(JNIEnv* env, jobject jobj, jint id, jfloat startX, jfloat startY, jfloat startZ, jfloat endX, jfloat endY, jfloat endZ)
{
jfloatArray ret = NULL;
float hitPoint[3];
float startPos[3] = { startX ,startY,startZ };
float endPos[3] = { endX ,endY,endZ };
dtPolyRef startRef = 0;
dtPolyRef endRef = 0;
float startNearestPt[3];
//float endNearestPt[3];
dtQueryFilter filter;
filter.setIncludeFlags(0xffff);
filter.setExcludeFlags(0);
NavMeshContex* navMeshContex = RecastGet(id);
if (navMeshContex == nullptr)
{
return ret;
}
//cout << navMeshContex->toString() << endl;
int dtStatus = navMeshContex->navQuery->findNearestPoly(startPos, raycastExtents, &filter, &startRef, startNearestPt);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
// dtStatus = navMeshContex->navQuery->findNearestPoly(endPos, raycastExtents, &filter, &endRef, endNearestPt);
// if ((dtStatus & DT_SUCCESS) == 0)
// {
// return ret;
// }
dtPolyRef polys[MAX_POLYS];
int npolys;
int nstraightPath = 0;
float t = 0;
float hitNormal[3];
memset(hitNormal, 0, sizeof(hitNormal));
//printf("version: %d\n", 3);
dtStatus = navMeshContex->navQuery->raycast(startRef, startNearestPt, endPos, &filter, &t, hitNormal, polys, &npolys, MAX_POLYS);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
// printf("endPos: %f\n", t);
if (t > 1) {
// no hit;
ret = Util::ConvertFloatStarToJfloatArray(env, endPos, 3);
return ret;
}
else {
// hit
hitPoint[0] = startPos[0] + (endPos[0] - startPos[0]) * t;
hitPoint[1] = startPos[1] + (endPos[1] - startPos[1]) * t;
hitPoint[2] = startPos[2] + (endPos[2] - startPos[2]) * t;
// printf("hitPoint: %f,%f,%f\n", hitPoint[0], hitPoint[1], hitPoint[2]);
// printf("npolys: %d\n", npolys);
ret = Util::ConvertFloatStarToJfloatArray(env, hitPoint, 3);
//if (npolys) {
// float h = 0;
// dtStatus = navMeshContex->navQuery->getPolyHeight(polys[npolys - 1], hitPoint, &h);
// if ((dtStatus & DT_SUCCESS) == 0)
// {
// return ret;
// }
// hitPoint[1] = h;
// ret = Util::ConvertFloatStarToJfloatArray(env, hitPoint, 3);
//}
}
return ret;
}
/**
* 找到目标点最近的静态可达点
*
* @param navmeshId 寻路数据地图ID
* @param pointX 参考点X
* @param pointY 参考点Y
* @return 如果目标点可达,返回目标点即可, 如果搜索范围内没有,返回空
*/
JNIEXPORT jfloatArray JNICALL Java_io_gamioo_nav_NavEngine_findNearest(JNIEnv* env, jobject jobj, jint id, jfloat pointX, jfloat pointY,jfloat pointZ)
{
jfloatArray ret = NULL;
float nearestPos[3]={0.0f};
float startPos[3] = { pointX ,pointY,pointZ };
dtPolyRef startRef = 0;
dtQueryFilter filter;
filter.setIncludeFlags(0xffff);
filter.setExcludeFlags(0);
NavMeshContex* navMeshContex = RecastGet(id);
if (navMeshContex == nullptr)
{
return ret;
}
int dtStatus = navMeshContex->navQuery->findNearestPoly(startPos, extents, &filter, &startRef, nearestPos);
if ((dtStatus & DT_SUCCESS) == 0)
{
return ret;
}
if (nearestPos[0]*10000==0&& nearestPos[1] * 10000 == 0 && nearestPos[2] * 10000 == 0) {
// printf("nearestPos: %f %f %f\n", nearestPos[0], nearestPos[1], nearestPos[2]);
return ret;
}
ret = Util::ConvertFloatStarToJfloatArray(env, nearestPos, 3);
return ret;
}
/**
* 释放加载的地图数据
*
* @param navmeshId 寻路数据地图ID
*/
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_release(JNIEnv* env, jobject jobj, jint id)
{
NavMesh::GetInstace()->Remove(id);
}
/**
* 释放加载的所有地图数据
*/
JNIEXPORT void JNICALL Java_io_gamioo_nav_NavEngine_releaseAll(JNIEnv* env, jobject jobj)
{
NavMesh::GetInstace()->Clear();
}
编译脚本 CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
find_package(JNI REQUIRED)
# Use C++11
set(CMAKE_CXX_STANDARD 11)
# Require (at least) it
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Don't use e.g. GNU extension (like -std=gnu++11) for portability
set(CMAKE_CXX_EXTENSIONS OFF)
include_directories(${JNI_INCLUDE_DIRS})
if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) )
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT" CACHE STRING "")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd" CACHE STRING "")
endif ()
project(RecastDll)
find_path(RecastDll_PROJECT_DIR NAMES SConstruct
PATHS
${CMAKE_SOURCE_DIR}
NO_DEFAULT_PATH
)
MARK_AS_ADVANCED(RecastDll_PROJECT_DIR)
# 配置cpp文件
file(GLOB RECASTDLL_SOURCES
Source/*.cpp
../Detour/Source/*.cpp
../DetourCrowd/Source/*.cpp
../DetourTileCache/Source/*.cpp
../Recast/Source/*.cpp
)
# 配置头文件
include_directories(
Include
../DebugUtils/Include
../Detour/Include
../DetourCrowd/Include
../DetourTileCache/Include
../Recast/Include
)
macro(source_group_by_dir proj_dir source_files)
if(MSVC)
get_filename_component(sgbd_cur_dir ${proj_dir} ABSOLUTE)
foreach(sgbd_file ${${source_files}})
get_filename_component(sgbd_abs_file ${sgbd_file} ABSOLUTE)
file(RELATIVE_PATH sgbd_fpath ${sgbd_cur_dir} ${sgbd_abs_file})
string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath})
string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup)
string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name})
if(sgbd_nogroup)
set(sgbd_group_name "\\")
endif(sgbd_nogroup)
source_group(${sgbd_group_name} FILES ${sgbd_file})
endforeach(sgbd_file)
endif(MSVC)
endmacro(source_group_by_dir)
source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} RECASTDLL_SOURCES)
add_library(RecastDll SHARED ${RECASTDLL_SOURCES})
if ( WIN32 AND NOT CYGWIN )
target_compile_definitions (RecastDll PRIVATE DLL_EXPORTS)
endif ( )
该回答引用ChatGPT
如有疑问,可以回复我!
这可能有多种原因,以下是一些可能性:
1、平台差异:Windows 和 Linux 有不同的系统架构和库,这可能会影响 JNI 代码的性能。可能 Windows 版本的代码在该平台上进行了更好的优化,而 Linux 版本则没有。
2、编译器差异:JNI 代码可能在 Windows 和 Linux 上使用不同的编译器编译,这可能会导致不同的优化水平和性能。
3、代码实现差异:在 Windows 和 Linux 上运行 JNI 代码的方式可能不同,这可能会影响代码的性能。可能 Windows 和 Linux 上的代码实现方式存在一些差异,从而导致它们在不同平台上的性能不同。
4、硬件差异:您在 Windows 和 Linux 上运行代码的计算机可能有不同的硬件配置,这可能会影响代码的性能。可能在 Windows 上运行代码的计算机更适合运行该代码,而在 Linux 上运行的计算机则不太适合。
参考GPT和自己的思路:根据您提供的信息,可以推测以下原因导致JNI在Linux下性能较差:
1 平台差异:JNI在不同操作系统上的性能表现可能不同,因为不同操作系统的底层架构和实现方式不同。您可以考虑对不同操作系统分别进行优化。
2 编译器优化:您可以尝试使用不同的编译器进行编译,或者调整编译器的优化级别,以获取更好的性能。
3 环境配置:Linux和Windows系统的环境配置可能不同,您需要确保在Linux系统上也正确地设置了所有必要的参数,如内存大小、线程池大小等等。
4 调用方式:您的JNI接口是如何被调用的?如果JNI接口在Linux下被频繁调用,那么可能会导致性能问题。您可以尝试将JNI接口调用频率降低,或者使用异步方式调用JNI接口。
5 其他因素:还有其他因素可能会导致性能差异,如操作系统版本、硬件配置、代码质量等等。您可以进一步排查这些因素,找出导致性能问题的根本原因。
以下是一个简单的 JNI 示例以及流程:
1 编写 C/C++ 代码实现功能,并编译成动态链接库(.dll 或 .so 文件)。
#include <jni.h>
JNIEXPORT jstring JNICALL Java_MyClass_hello(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "Hello, World!");
}
2 在 Java 中声明 native 方法,并加载动态链接库。
public class MyClass {
public native String hello();
static {
System.loadLibrary("mylibrary");
}
}
3 生成 JNI 头文件。
在 C/C++ 中,可以使用 javah 工具自动生成 JNI 头文件。在命令行中进入包含 Java 类的目录,然后输入以下命令:
javah -jni MyClass
将生成名为 MyClass.h 的头文件,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class MyClass */
#ifndef _Included_MyClass
#define _Included_MyClass
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyClass
* Method: hello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_MyClass_hello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
4 实现 JNI 方法。
在 C/C++ 中,可以使用 jclass 和 jmethodID 类型的参数来访问 Java 类和方法。JNIEnv 结构体提供了许多有用的函数来访问 Java 对象、数组等。
#include <jni.h>
JNIEXPORT jstring JNICALL Java_MyClass_hello(JNIEnv *env, jobject obj) {
return (*env)->NewStringUTF(env, "Hello, World!");
}
5 编译动态链接库。
使用相应的编译器和链接器编译动态链接库。在 Windows 上,使用 MSVC 或 MinGW,将输出一个 .dll 文件。在 Linux 上,使用 GCC 或 Clang,将输出一个 .so 文件。
6 运行程序。
将生成的动态链接库文件复制到 Java 程序所在的目录下,然后运行程序即可。
public static void main(String[] args) {
MyClass myObj = new MyClass();
System.out.println(myObj.hello()); // 输出 "Hello, World!"
}
这是一个简单的 JNI 示例,实现了一个简单的 hello 方法,返回一个字符串。实际上,JNI 可以访问 Java 对象、数组等,并在 C/C++ 中创建、释放它们。在实际应用中,需要谨慎处理内存管理等问题。
这可能是由于不同操作系统的JNI实现方式不同导致的。在Windows下,JNI使用的是Microsoft Visual C++编译器,而在Linux下,JNI使用的是GNU C++编译器。这两种编译器的实现方式不同,可能会导致性能上的差异。此外,不同操作系统的内存管理方式也不同,也可能会影响JNI性能。建议在Linux下进行性能调优。
我猜得,不知道对不对!,我也是新手
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
这个问题可能会涉及到很多因素,比如编译器,操作系统,线程模型,内存管理等等。下面简单列举一些可能影响 JNI 在不同操作系统下性能的因素:
不同操作系统下,可能会使用不同的编译器,并且这些编译器可能采用不同的优化策略。在不同的编译器下,同样的代码可能会被优化成不同的汇编代码,从而影响JNI性能。
JNI 和 Java 代码之间需要频繁地传递数据,这个数据传递可能涉及到内存管理和垃圾回收。不同操作系统下内存模型可能不同,这也可能影响 JNI 的性能。此外,Linux 在处理垃圾回收方面相对于 Windows 会更具优势,因为 Linux 内核提供了更高级别的虚拟内存管理和页表机制。
JNI 在 Windows 系统下,线程模型为 Win32 线程模型,而在 Linux 下是 POSIX 线程模型。这两种线程模型有不同的实现方式,可能导致JNI在不同系统下性能表现不一致。
JNI代码在 CPU 上运行,每个 CPU 架构和指令集都会对 JNI 执行效率产生不同的影响。比如,SSE2 指令集可以使得浮点运算在 x86 架构下更加高效。
总之,对于 JNI 在不同操作系统下性能表现不一致的具体原因,需要进一步了解你的代码和系统环境。不过,可以尝试几个方向进行优化:
因为没有代码,我无法给出具体的优化建议。最好在工具或者 IDE 下面 profile 一下你的代码,找到性能瓶颈所在,并基于此做出优化。
如果我的回答解决了您的问题,请采纳!