美文网首页
C++启动JVM

C++启动JVM

作者: 文泰ChrisTwain | 来源:发表于2022-09-27 22:36 被阅读0次

1.业务背景

通过C++进程可直接加载JVM并运行Jar里面的Java代码,也就是通过C++的进程,比如一个exe加载一个JVM,JVM里面再通过JNI实现java代码与c++代码互调,可实现java程序直接运行在exe。当然有装JDK,jar包也能直接启动。
官网API指导https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html

2.环境准备

(1)新建Java工程,生成Jar包
(2)新建Visual Studio c++工程
(3)下载windows JDK,拷贝\jdk1.8.0_202\jre\bin和\jdk1.8.0_202\jre\lib作为c++工程JVM的依赖,以及导入\jdk1.8.0_202\include中的jni.h和jni_md.h作为头文件

3.代码实现

(1)启动JVM

// 以下两个变量初始化JVM时赋值,可保存成全局变量方便使用
static JavaVM *jvm = NULL;  \\ 虚拟机在JNI层的代表,可用于获取线程对应的JNIEnv
static JNIEnv *env = NULL;   \\ 这里表示主线程JNIEnv(通常调用JNI_CreateJavaVM创建JVM的线程被称为主线程),封装JNI方法的指针,不同线程彼此独立且每个线程只有一个JNIEnv

bool initJvm()
{
    std::string filePath = util::getCurrentPath().append("\\MyJars\\");
    std::vector<std::string> files;
    // 获取该路径下的所有Jar文件,若有多个jar使用分号;分隔
    util::getFiles(filePath, files);
    int size = files.size();

    std::string jarPath;
    for (int i = 0; i < size; i++)
    {
        if (i == size - 1) {
            jarPath.append(files[i].c_str());
        } else {
            jarPath.append(files[i].c_str()).append(";");
        }
    }

    // JDK下jvm动态库的路径
    std::string dllPath = "D:\\Java\\jdk1.8.0_202\\jre\\bin\\server\\jvm.dll";

    // java虚拟机启动时接收的参数,每个参数单独一项
    int nOptionCount = 4;
    JavaVMOption vmOption[4];
    // 设置JVM最大允许分配的堆内存,按需分配
    vmOption[0].optionString = (char *)"-Xmx256M";
    // 设置classpath
    std::string jar_path = std::string("-Djava.class.path=").append(jarPath);
    vmOption[1].optionString = (char *)jar_path.c_str();
    vmOption[2].optionString = (char *)"-Xtrace";
    vmOption[3].optionString = (char *)"-XX:+CreateMinidumpOnCrash";
    JavaVMInitArgs vmInitArgs;
    vmInitArgs.version = JNI_VERSION_1_8;
    vmInitArgs.options = vmOption;
    vmInitArgs.nOptions = nOptionCount;

    // 忽略无法识别jvm的情况
    vmInitArgs.ignoreUnrecognized = JNI_TRUE;
    HINSTANCE jvmDLL = LoadLibraryA(dllPath.c_str());
    JNICREATEPROC jvmProcAddress = (JNICREATEPROC)GetProcAddress(jvmDLL, "JNI_CreateJavaVM");
    if (!jvmProcAddress) {
        FreeLibrary(jvmDLL);
        return false;
    }

    // 初始化JVM
    jint jvmProc = (jvmProcAddress)(&jvm, (void **)&env, &vmInitArgs);
    if (jvmProc < 0 || jvm == NULL || env == NULL) {
        FreeLibrary(jvmDLL);
        return false;
    }
    return true;
}

Util.h工具类

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <shlwapi.h>
#include <io.h>
#include <vector> 
#pragma comment(lib, "Shlwapi.lib")

class util {
public:
    static std::string getCurrentPath()
    {
        char filePath[MAX_PATH] = { 0 };
        GetModuleFileNameA(NULL, filePath, MAX_PATH);
        PathRemoveFileSpecA(filePath);
        return filePath;
    }

    static void getFiles(std::string path, std::vector<std::string>& files)
    {
        // 文件句柄
        intptr_t hFile = 0;
        // 文件信息
        struct _finddata_t fileinfo;
        std::string p;
        if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
        {
            do
            {
                if ((fileinfo.attrib &  _A_SUBDIR))
                {
                    if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                        getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
                }
                else
                {
                    files.push_back(p.assign(path).append("\\").append(fileinfo.name));
                }
            } while (_findnext(hFile, &fileinfo) == 0);
            _findclose(hFile);
        }
    }
};

(2)加载Java启动类并启动Main方法

void callMain()
{
    const char* className = "com/test/HelloWorld";
    const char* methodName = "main";
    const char* methodSign = "([Ljava/lang/String;)V";

    JNIEnv *subEnv = NULL;
    if (Get_env(&subEnv)) {
        //LOGGER_DEBUG(L"get_env fail");
        return;
    }
    jclass subEnv = subEnv->FindClass(className);
    if (subEnv->ExceptionCheck() == JNI_TRUE || targetClass == NULL) {
        subEnv->ExceptionDescribe();
        subEnv->ExceptionClear();
        //LOGGER_DEBUG(L"find class fail");
        return;
    }
    jmethodID targetMethod = env->GetStaticMethodID(targetClass, methodName, methodSign);
    if (subEnv->ExceptionCheck() == JNI_TRUE || targetMethod == NULL) {
        subEnv->ExceptionDescribe();
        subEnv->ExceptionClear();
        //LOGGER_DEBUG(L"find method fail");
        return;
    }

    if (subEnv != NULL && targetClass != NULL && targetMethod != NULL) {
        subEnv->CallStaticVoidMethod(targetClass, targetMethod);
        Release_env(subEnv);
    }
}

获取线程对应的JNIEnv

// 若为主线程可直接使用初始化JVM时保留的env变量,判断主线程可先保存主线程的线程号,当调用此方法是通过GetCurrentThreadId()判断是否当前为主线程
bool Get_env(JNIEnv **env)
{
    int status = -1;
    if (jvm != NULL) {
        status = jvm->GetEnv((void**)env, JNI_VERSION_1_8);
    } else {
        // LOGGER_DEBUG(L"jvm is NULL");
        return false;
    }
    if (status == JNI_EDETACHED) {
        status = jvm->AttachCurrentThread((void **)env, NULL);
    }
    if (status != JNI_OK) {
        // LOGGER_DEBUG(L"get_env FAILED");
        return false;
    }
    return true;
}


void Release_env(JNIEnv *env)
{
    int status = jvm->GetEnv((void**)&env, JNI_VERSION_1_8);
    if (status == JNI_OK) {
        jvm->DetachCurrentThread();
    } else {
        // LOGGER_DEBUG(L"NEED NOT DETACH");
    }
}

释放资源

MyClass::~MyClass()
{
    jvm->DetachCurrentThread();
    jvm->DestroyJavaVM();
    FreeLibrary(jvmDLL);
}

4.内存测试

exe加载完JVM后内存大概10M,若jar包很多,内存将更大

参考:
C/C++启动JVM
浅谈JNIEnv和JavaVM

相关文章

  • C++启动JVM

    1.业务背景 通过C++进程可直接加载JVM并运行Jar里面的Java代码,也就是通过C++的进程,比如一个exe...

  • 类加载器子系统之类加载器(二)

    类加载器 JVM中有两种类型的类加载器,由C++编写的以及由Java编写的。除了启动类加载器(Bootstrap ...

  • 深入JVM内核原理-2.JVM运行机制

    1.JVM启动流程 JVM启动流程.png 2.JVM基本结构 JVM基本结构.png PC寄存器每个线程拥有一个...

  • chapter-2 jvm运行机制

    本章知识点 1.JVM启动流程2.JVM基本结构3.内存模型4.编译和解释运行的概念 JVM启动流程 JVM基...

  • Java 虚拟机的运行机制,理解 Java 内存模型

    JVM启动流程: 1、java+xxx启动虚拟机 2、jvm查找虚拟机的配置jvm.config 3、根据配置找到...

  • Android 中级(待续)

    SurfaceView 蓝牙 Android Binder 机制-JVM-JVM源码分析之JVM启动流程 Art ...

  • JVM核心知识

    1、JVM启动模式 jvm 有两种启动模式:Client 模式、Server 模式。 Client 模式:加载速度...

  • 第二周 JVM运行机制

    笔记 JVM启动流程启动过程如下图所示:bootup.png注释:jvm.cfg的用途:Controls the ...

  • Classloader 分析

    JVM启动过程 虚拟机申请完内存,就创建引导类加载器,引导类加载器是c++语言实现的,java无法获取,负责加载运...

  • native方法调用流程剖析

    JVM启动阶段 JVM启动时,会为native方法生成解释器入口,调用链为Threads::create_vm->...

网友评论

      本文标题:C++启动JVM

      本文链接:https://www.haomeiwen.com/subject/zhpfartx.html