¿Por qué Java no utiliza GC de conteo de referencias?

Su pregunta contiene varios mitos de administración de memoria:

> El objeto se liberará tan pronto como no tenga referencia.

La gente suele afirmar eso, pero no es cierto. Ver: mitos sobre la gestión de la memoria: rapidez

> El conteo de referencias basado en GC no causará un alto.

La gente a menudo afirma eso, particularmente en el contexto del recolector de basura ARC de Apple, pero tampoco es cierto. Cuando se disminuye un recuento de referencia, los recuentos de referencia del cierre transitivo de todos los objetos accesibles desde él, pero también se disminuyen. En la mayoría de las implementaciones, esto se hace inmediatamente en un ciclo cerrado. Por lo tanto, cuando la última referencia a una colección compuesta por un gran número de objetos asignados al montón cae fuera de alcance, el recolector de basura de conteo de referencias se encuentra allí, decrementando todos sus recuentos y recuperando toda su memoria. Eso incurre en una pausa ilimitada, potencialmente más larga que cualquier pausa en la que pueda incurrir un colector de rastreo generacional incremental típico.

Puede ver este efecto en Mathematica (que se cuenta por referencia) si crea un árbol grande y le pone una referencia desde un símbolo y luego establece el símbolo en nulo, Mathematica se detendrá (a veces por segundos) mientras su colector de basura cuenta de referencia Limpia toda la memoria. Cuando se usa Mathematica, no es raro que se detenga por largos períodos de tiempo mientras hace un recuento de referencias y es muy frustrante. Si compara con OCaml (que utiliza un recolector de basura de rastreo), las pausas en las que OCaml incurre son mucho más cortas que las de Mathematica.

En cuanto a su pregunta principal:

> ¿Por qué Java no utiliza GC de conteo de referencias?

La respuesta es porque el seguimiento de la recolección de basura es mejor para la mayoría de los programas y la mayoría de los programadores la mayor parte del tiempo. Las dos desventajas principales del recuento de referencias son que es muy lento (vea shared_ptr de Boost hasta 10 veces más lento que la recolección de basura de OCaml) y que pierde ciclos. Puede acelerar el conteo de referencias pero solo sacrificando sus otros beneficios (por ejemplo, la rapidez).

Ver: ¿Por qué el conteo automático de referencias en el Objetivo C es mucho más eficiente que la recolección de basura JVM?

Ni siquiera debería intentar ejecutar algo que dependa del tiempo en Java. No importa que incluso Google eligió Java para Android, y los teléfonos son dispositivos en tiempo real. Java es divertido, no tienes que escribir destructores o encontrar pérdidas de memoria. Todo está bien hasta que llega el momento de pagar toda esa diversión, cuando el recolector de basura tiene que correr.

El recuento de referencias también tiene algunos costos: cada asignación y copia y entrada y salida de funciones deben tener un código agregado para ajustar los recuentos de referencia. El código se hace más grande y usted incurre en costos de tiempo a lo largo del camino, en lugar de agruparlos a la hora de GC. Me inclino más hacia el conteo de referencias, pero la gente de google thgat eligió Java y eligió GC para golang, obviamente no son tontos y tenían sus razones. Supongo que la conclusión es que no hay almuerzo gratis. Puede pagar cada segundo o esperar hasta la hora del almuerzo para pagar a lo grande el almuerzo.