2016-02-23 1 views
2

J'ai reconstruit mon projet Java/C++ plusieurs fois en utilisant JNI, JNA, BridJ et JavaCPP, et chaque fois que je rencontre des fautes de segmentation aléatoires (imprévisibles). J'ai vérifié qu'un exécutable pur-C++ utilisant cette bibliothèque ne provoque jamais de fautes de segmentation, et dans le cas de BridJ, il l'a réduit au garbage collector de Java en l'invoquant explicitement.Erreurs de segmentation dans JNA/BridJ etc

Une pensée était que ces bibliothèques créent des objets pointeur Java côté qui appellent free ou delete quand ils déchets collectés (par finalize) au lieu de traiter les pointeurs qui renvoie C++ comme des références empruntées, comme ils le devraient dans cette application.

Mais j'ai essayé un test supplémentaire (non représenté ci-dessous): J'ai transformé chaque pointeur de l'API en int64_t (long en Java) et explicitement cast comme le type approprié en C++. Je vois encore les rares segfaults. Donc, ma question est large: qu'est-ce qui pourrait causer ce problème? J'accepterai des réponses en utilisant JNA ou BridJ parce que je peux facilement basculer entre les deux. Je pense qu'il me manque un problème fondamental parce que ce problème est si répandu dans toutes les bibliothèques que j'ai essayées.

Pour le concret, voici ma version JNA. Je suis en lien avec la bibliothèque CERN ROOT. RootTreeReader.h est:

#ifndef ROOTTREEREADER_H 
#define ROOTTREEREADER_H 

#include <TFile.h> 
#include <TTreeReader.h> 
#include <TTreeReaderValue.h> 
#include <TTreeReaderArray.h> 

using namespace ROOT::Internal; 

extern "C" { 
    TFile *newFile(const char *fileLocation); 
    TTreeReader *newReader(TFile *file, const char *treeLocation); 
    bool readerNext(TTreeReader *reader); 

    TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name); 

    float getValue_float(TTreeReaderValueBase *value); 
} 

#endif // ROOTTREEREADER_H 

RootTreeReader.cpp est:

#include <string> 

#include "RootTreeReader.h" 

TFile *newFile(const char *fileLocation) { 
    return TFile::Open(fileLocation); 
} 

TTreeReader *newReader(TFile *file, const char *treeLocation) { 
    return new TTreeReader(treeLocation, file); 
} 

bool readerNext(TTreeReader *reader) { 
    return reader->Next(); 
} 

TTreeReaderValueBase *newValue_float(TTreeReader *reader, const char *name) { 
    return new TTreeReaderValue<float>(*reader, name); 
} 

float getValue_float(TTreeReaderValueBase *value) { 
    return *((float*)value->GetAddress()); 
} 

Leur Makefile est

all: RootTreeReader.cpp 
    mkdir -p ../../../target/native/linux-x86-64 
    g++ RootTreeReader.cpp -o ../../../target/native/linux-x86-64/libRootTreeReader.so \ 
     -fPIC -shared \ 
     -Wl,--no-as-needed $(shell root-config --cflags --ldflags --libs) -lTreePlayer 

JNAerator génère le RootTreeReaderLibrary.java suivant:

package org.dianahep.scaroot; 
import com.sun.jna.Library; 
import com.sun.jna.Native; 
import com.sun.jna.NativeLibrary; 
import com.sun.jna.Pointer; 
import com.sun.jna.PointerType; 
/** 
* JNA Wrapper for library <b>RootTreeReader</b><br> 
* This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br> 
* a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br> 
* For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>. 
*/ 
public interface RootTreeReaderLibrary extends Library { 
    public static final String JNA_LIBRARY_NAME = "RootTreeReader"; 
    public static final NativeLibrary JNA_NATIVE_LIB = NativeLibrary.getInstance(RootTreeReaderLibrary.JNA_LIBRARY_NAME); 
    public static final RootTreeReaderLibrary INSTANCE = (RootTreeReaderLibrary)Native.loadLibrary(RootTreeReaderLibrary.JNA_LIBRARY_NAME, RootTreeReaderLibrary.class); 
    /** 
    * Original signature : <code>TFile* newFile(const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:5</i><br> 
    * @deprecated use the safer methods {@link #newFile(java.lang.String)} and {@link #newFile(com.sun.jna.Pointer)} instead 
    */ 
    @Deprecated 
    RootTreeReaderLibrary.TFile newFile(Pointer fileLocation); 
    /** 
    * Original signature : <code>TFile* newFile(const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:5</i> 
    */ 
    RootTreeReaderLibrary.TFile newFile(String fileLocation); 
    /** 
    * Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:6</i><br> 
    * @deprecated use the safer methods {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, java.lang.String)} and {@link #newReader(org.dianahep.scaroot.RootTreeReaderLibrary.TFile, com.sun.jna.Pointer)} instead 
    */ 
    @Deprecated 
    RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, Pointer treeLocation); 
    /** 
    * Original signature : <code>TTreeReader* newReader(TFile*, const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:6</i> 
    */ 
    RootTreeReaderLibrary.TTreeReader newReader(RootTreeReaderLibrary.TFile file, String treeLocation); 
    /** 
    * Original signature : <code>bool readerNext(TTreeReader*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:7</i> 
    */ 
    byte readerNext(RootTreeReaderLibrary.TTreeReader reader); 
    /** 
    * Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:9</i><br> 
    * @deprecated use the safer methods {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, java.lang.String)} and {@link #newValue_float(org.dianahep.scaroot.RootTreeReaderLibrary.TTreeReader, com.sun.jna.Pointer)} instead 
    */ 
    @Deprecated 
    RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, Pointer name); 
    /** 
    * Original signature : <code>TTreeReaderValueBase* newValue_float(TTreeReader*, const char*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:9</i> 
    */ 
    RootTreeReaderLibrary.TTreeReaderValueBase newValue_float(RootTreeReaderLibrary.TTreeReader reader, String name); 
    /** 
    * Original signature : <code>float getValue_float(TTreeReaderValueBase*)</code><br> 
    * <i>native declaration : src/main/cpp/RootTreeReader.h:11</i> 
    */ 
    float getValue_float(RootTreeReaderLibrary.TTreeReaderValueBase value); 
    public static class TFile extends PointerType { 
     public TFile(Pointer address) { 
      super(address); 
     } 
     public TFile() { 
      super(); 
     } 
    }; 
    public static class TTreeReader extends PointerType { 
     public TTreeReader(Pointer address) { 
      super(address); 
     } 
     public TTreeReader() { 
      super(); 
     } 
    }; 
    public static class TTreeReaderValueBase extends PointerType { 
     public TTreeReaderValueBase(Pointer address) { 
      super(address); 
     } 
     public TTreeReaderValueBase() { 
      super(); 
     } 
    }; 
} 

et Je l'appelle comme ceci:

Native.setProtected(true) 
println("Native.isProtected", Native.isProtected) // it's true on Linux; I've tried this with and without 

val lib = RootTreeReaderLibrary.INSTANCE 

println("one") 
val file = lib.newFile("TrackResonanceNtuple.root") 
println("two") 
val reader = lib.newReader(file, "TrackResonanceNtuple/twoMuon") 
println("three") 
val mass = lib.newValue_float(reader, "mass_mumu") 
println("four") 
var counter = 0 
while (lib.readerNext(reader) > 0) { 
    val num = lib.getValue_float(mass) 
    println(num) 
    counter += 1 
    // if (counter % 1000 == 0) { 
    // println("gc start") 
    // System.gc() 
    // println("gc end") 
    // } 
} 
println("five") 

Il segfaults rarement avec ou sans invocations garbage collector explicites. Une version BridJ de cette segfaults rarement sans et fréquemment avec invocations de garbage collector explicite.

+0

JNA ne libère que la mémoire allouée, et ce dans des emplacements très bien définis (voir 'Native.dispose()', qui les référence tous). Vous pouvez désactiver sélectivement chacune de ces méthodes d'élimination pour déterminer si l'une d'entre elles est impliquée. – technomage

+0

Auriez-vous la version de JavaCPP que je pourrais jeter un coup d'oeil? –

+0

J'ai essayé JavaCPP 1.1, bien que l'exemple ci-dessus soit JNA. –

Répondre

4

La solution rapide:

export LD_PRELOAD=/path/to/libjsig.so 

Le problème est que le CERN ROOT tente d'installer des gestionnaires pour les mêmes signaux que le JDK.Oracle recommande de précharger libjsig.so, qui fournit une "installation de signal enchaînant", pour contourner ce genre de questions:

https://docs.oracle.com/javase/6/docs/technotes/guides/vm/signal-chaining.html

Modifier de Jim:

Plus précisément pour le cadre de ROOT, vous peut désactiver la gestion du signal en appelant

gSystem->ResetSignals(); 

La discussion se trouvent sur ce message ROOT Conseil: https://root.cern.ch/phpBB3/viewtopic.php?t=8231