Modelo de memoria J2ObjC

En este documento, se describe cómo se administra la memoria con el código traducido de J2ObjC y cómo se comportan los programas cuando se accede a la memoria compartida.

Administración de la memoria

Uno de los objetivos de J2ObjC es producir un código traducido que se integre sin problemas en el entorno de recuento de referencias de Objective-C. Esto hace que el código Java traducido es fácil de usar desde Objective-C escrito de forma nativa, ya que no hay una transferencia de propiedad incómoda para los objetos que se pasan entre los entornos de Java y Objective-C.

Debido a que Java usa la recolección de elementos no utilizados para la administración de memoria, el código Java no contiene una administración de memoria explícita para sus objetos. Por lo tanto, J2ObjC debe insertar llamadas de recuento de referencias de forma correcta para garantizar que los objetos se desasignan en el momento correcto. Nos establecimos el siguiente conjunto de reglas que consideramos que tienen buen rendimiento y son prácticas:

  • Todos los objetos estarán activos durante al menos el tiempo que dure el grupo de versiones automáticas actual.
    • Esta regla general nos permite omitir muchas conservaciones y actualizaciones que, de otro modo, serían necesarias.
  • No se retienen las variables locales.
    • No hay referencias de recuento de llamadas en lecturas o escrituras de variables locales.
  • Se conservan los campos.
    • La asignación de las llamadas de campo se conservan en el valor nuevo y se liberan automáticamente en el valor anterior.
  • Los objetos nuevos se liberan automáticamente de inmediato. (a menos que se asigne inmediatamente a un campo)

Ciclos de referencia

Un ciclo de referencia existe cuando un objeto se refiere a sí mismo de forma directa o indirecta a través de sus campos. La recolección de elementos no utilizados de Java puede limpiar los ciclos de referencia, pero generará una pérdida de memoria en el entorno de recuento de referencias de Objective-C. No existe una forma automatizada de evitar que ocurran ciclos de referencia. Sin embargo, proporcionamos una herramienta Cycle Finder que automatiza la detección de ciclos. Estas son algunas formas comunes de corregir un ciclo de referencia:

  • Agrega una anotación @Weak o @WeakOuter para debilitar una de las referencias.
  • Agrega un método cleanup() a uno de los objetos que establezca algunos campos como nulos. Llama a cleanup() antes de descartar el objeto.
  • Rediseñar el código para evitar crear un ciclo de referencia por completo.

Memoria compartida

En un programa multiproceso, algunos datos se pueden compartir mediante varios subprocesos. Java proporciona varias herramientas para permitir el acceso seguro a los subprocesos a los datos compartidos. En esta sección, se describe la compatibilidad de J2ObjC para acceder a datos compartidos.

Sincronizado

J2ObjC asigna la palabra clave synchronized directamente a @synchronized de Objective-C.

Atomicidad

Java garantiza la atomicidad para cargas y almacenes de todos los tipos, excepto long y double. Consulta JLS-17.7. A excepción de los campos volatile (que se describen a continuación), J2ObjC no proporciona un tratamiento especial para garantizar las cargas y los almacenes atómicas. Esto implica lo siguiente:

  • Como todas las plataformas de iOS son de 32 o 64 bits, las cargas y los almacenes de tipos primitivos, excepto long y double, son atómicas en dispositivos de 32 bits, y todas son atómicas en sistemas de 64 bits.
  • Las cargas y los almacenamientos de tipos de objetos no son atómicos en J2ObjC.
    • La actualización atómica de los recuentos de referencias es demasiado costosa.
    • Un campo de objeto se puede convertir en atómico si se lo declara volatile. (consulta a continuación)

Campos volátiles

Para los campos volatile, Java proporciona atomicidad y orden con coherencia secuencial (JLS-8.3.1.4), que se puede usar en la sincronización. J2ObjC proporciona las mismas garantías que Java para todos los campos volatile. J2ObjC usa los siguientes mecanismos para los campos volatile:

  • Los tipos primitivos se asignan a los tipos atómicos de c11.
    • p. ej., volatile int -> _Atomic(jint)
  • Los campos de objetos están protegidos con bloqueos de exclusión mutua de pthread. (no puede usar bloqueos de rotación debido a la inversión de prioridad)
    • La exclusión mutua es necesaria para evitar condiciones de carrera con el recuento de referencias.
    • La implementación es muy similar a las propiedades atómicas de Objective-C.

Tipos atómicos

Java proporciona una serie de tipos atómicos en el paquete java.util.concurrent.atomic. Todos estos son totalmente compatibles con J2ObjC con implementaciones personalizadas.

Campos finales

Java garantiza que un subproceso vea los valores inicializados para los campos finales de un objeto sin requerir ninguna sincronización cuando se comparte el objeto. (JSL-17.5). Sin embargo, dado que J2ObjC no admite el acceso atómico de tipos de objetos no volátiles (ver arriba), no hay una forma segura de compartir un objeto sin sincronización. Por lo tanto, no se aplican restricciones adicionales al usuario de J2ObjC omitiendo las vallas de memoria necesarias para los campos finales.