2
votes

My C++ code, push a string to mystack

#include <iostream>
#include <stack>
#include "NativeLogger.h"


std::stack<std::string> mystack;


JNIEXPORT void JNICALL
Java_NativeLogger_push(JNIEnv *env, jobject obj,jstring name)
{

  std::string s = env->GetStringUTFChars(name, 0);

  mystack.push(s);

  return;
}

JNIEXPORT void JNICALL
Java_NativeLogger_pop(JNIEnv *env, jobject obj)
{

  mystack.pop();
  return;
}

I got follow crash report when runs with Java, any idea how to fix it?

A fatal error has been detected by the Java Runtime Environment:

SIGSEGV (0xb) at pc=0x00007f29421a0207, pid=18007, tid=0x00007f2942d3e700

JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01) Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops) Problematic frame: C [libc.so.6+0x97207] __libc_malloc+0x197

2

2 Answers

1
votes

It's faily likely that GetStringUTFChars returned a pointer to a copy of the actual string, which you promptly discarded by passing it directly as an argument to std::string's constructor, thereby causing a memory leak.

You need to hang on to the pointer in order to be able to free it:

const char *p = env->GetStringUTFChars(name, NULL);
std::string s(p);
env->ReleaseStringUTFChars(name, p);

Note that even in the unlikely case where no copy was made, you still need to call ReleaseStringUTFChars, because the VM might've pinned the Java string in memory which could interfere with the garbage collector.

1
votes

Java code:

package recipeNo025;

public class HelloWorld {

  public static native void pushString(String s);
  public static native String popString();

  static {
    System.loadLibrary("HelloWorld");
  }

  public static void main(String[] args) {
    HelloWorld.pushString("Hello");
    System.out.println(HelloWorld.popString());
  }
}

C++ code

#include <iostream>
#include <stack>
#include "jni.h"
#include "recipeNo025_HelloWorld.h"

std::stack<std::string> mystack;

JNIEXPORT void JNICALL Java_recipeNo025_HelloWorld_pushString
  (JNIEnv *env, jclass obj, jstring str) {

    // we have to get string bytes into C string
    const char *c_str;
    c_str = env->GetStringUTFChars(str, NULL);
    if(c_str == NULL) {
        return;
    }

    std::cout << "Passed string: " << c_str << std::endl;

    std::string my_string(c_str);
    mystack.push(my_string);

    // after using it, remember to release the memory
    env->ReleaseStringUTFChars(str, c_str);
}

JNIEXPORT jstring JNICALL Java_recipeNo025_HelloWorld_popString
  (JNIEnv *env, jclass obj) {

    std::string s = mystack.top();
    mystack.pop();
    return env->NewStringUTF(s.c_str());
}

Execution:

> java -Djava.library.path=:./lib -cp target recipeNo025.HelloWorld
Passed string: Hello
Hello

Also, I'd have considered using Singleton pattern instead of having global variable for stack.