Pregunta

Pensé que esta pregunta se habría hecho antes, pero no pude encontrarla aquí ...

He usado SWIG para crear un contenedor JNI alrededor de una clase C ++. Todo funciona muy bien, excepto que Java nunca parece llamar a finalize () de la clase, por lo que, a su vez, nunca se llama al destructor de mi clase. El destructor de la clase realiza algunas E / S de archivo final, por lo que desafortunadamente, esto no es solo una pérdida menor de memoria.

Al buscar en Google, no parece haber una forma de forzar a Java a GC y destruir un objeto. ¿Verdadero?

Sé que podría manipular mi archivo SWIG y crear una función Java que llamaría al destructor de C ++, pero esta clase es utilizada por los usuarios finales en varias plataformas / lenguajes diferentes, por lo que la adición de solo Java creará una inconsistencia que a nuestros escritores tecnológicos no les va a gustar.

¿Fue útil?

Solución

No puede forzar GC con System.gc (). Tampoco está garantizado que alguna vez se ejecute un GC, por ejemplo, si su aplicación se ejecuta solo por un corto tiempo y luego su finalizador no se ejecutará en absoluto (la JVM no se ejecuta al salir). Debe crear un close () o destroy () o cualquier función para su clase y cuando termine de usar una instancia de esta clase, instálela, preferiblemente desde un bloque finalmente, como.


MyClass x = null;
try{
    x = new MyClass();
    x.work();
} finally {
    if (x!=null)
        x.close();
}

Otros consejos

Los finalizadores de Java son en su mayoría inútiles, en mi opinión, y ciertamente no son un reemplazo para los destructores de C ++. Desafortunadamente, Java no tiene reemplazo para C ++ RAII.

No te molestes en intentar forzar la finalización de Java. Cuando haya terminado con lo que sea, toda una función que lo eliminará. Eso es todo lo que puedes hacer.

Si confía en el código del método finalize para ejecutarse en un momento determinado, debe reconsiderar su enfoque. El problema aquí es que no sabe cuándo JVM llamará a finalize , ya que no sabe cuándo se recolectará el objeto.

Una cosa que debe tener en cuenta, ya que su clase se reutilizará en otros proyectos, es que es posible que un usuario final pueda usar una instancia de una clase de tal manera que no se recopile o que La recolección de basura será poco probable, como crear una referencia estática a una instancia de la clase. Creo que crear un método close o destroy es su apuesta más segura para garantizar que los recursos que utiliza la instancia de la clase C ++ asociada con su objeto Java se liberen adecuadamente.

Dado que la reutilización es una preocupación, puede hacer que el destructor de C ++ verifique si los recursos se han liberado y llamar al mismo código si no lo hubieran sido, así:

class MyThing {
  public:
    void close();
    ~MyThing();

  private:
    bool released = false;
};

void
MyThing::close() {
  // close logic here
  this->released = true;
}

MyThing::~MyThing() {
  if (!released) {
    this->close();
  }
}

De esta manera, es de esperar que su código C ++ existente no tenga que cambiar mucho, y puede asegurarse de que sus recursos se liberen de manera determinista en el contexto del código nativo que se ejecuta a través de JNI.

Después de mirar un poco más el código producido por SWIG, veo que la gente de SWIG ya se ha ocupado de esto: agregan una función delete () para usted. Parece que se ha cuidado bastante bien la posibilidad de que tanto el programador como el GC eliminen el objeto también.

Problemas como este son la razón por la que C # eligió el IDisposable patrón para la finalización determinista.

Le sugiero que siga el mismo patrón y lo adapte para sus usuarios de Java.

En su clase c ++, cree un método público separado que elimine sus recursos. Llámalo cerca, o deséchalo, o algo así.

Haga que su destructor de C ++ llame al método público y diga a los usuarios administrados / GC de la clase C ++ que deben llamar al método para evitar pérdidas de memoria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top