que objetos metodo memoria guardar forzar example como collector activar java arrays memory jvm int

metodo - guardar objetos en memoria java



Uso de la memoria Java interna (7)

Mientras pensaba en el uso de memoria de varios tipos, comencé a confundirme un poco sobre cómo Java utiliza la memoria para los enteros cuando se pasa a un método.

Diga, tenía el siguiente código:

public static void main (String[] args){ int i = 4; addUp(i); } public static int addUp(int i){ if(i == 0) return 0; else return addUp(i - 1); }

En este siguiente ejemplo, me pregunto si mi siguiente lógica era correcta:

  • Inicialmente hice una memoria para el entero i = 4. Luego la paso a un método. Sin embargo, como las primitivas no apuntan en Java, en el addUp (i == 4), creo otro entero i = 4. Luego, hay otro addUp (i == 3), addUp (i == 2), addUp (i == 1), addUp (i == 0) en el que cada vez, dado que el valor no es puntiagudo, se asigna un nuevo valor i en la memoria.
  • Luego, para un único valor "int i", he usado 6 memorias de valores enteros.

Sin embargo, si tuviera que pasarlo siempre a través de una matriz:

public static void main (String[] args){ int[] i = {4}; // int tempI = i[0]; addUp(i); } public static int addUp(int[] i){ if(i[0] == 0) return 0; else return addUp(i[0] = i[0] - 1); }

- Como creo una matriz de enteros de tamaño 1 y luego la paso a addUp, que se pasará de nuevo para addUp (i [0] == 3), addUp (i [0] == 2), addUp (i [0] == 1), addUp (i [0] == 0), solo he tenido que usar 1 espacio de memoria de matriz entera y, por lo tanto, mucho más rentable. Además, si tuviera que hacer un valor int de antemano para almacenar el valor inicial de i [0], todavía tengo mi valor "original".

Entonces, esto me lleva a la pregunta, ¿por qué la gente pasa primitivos como int en los métodos de Java? ¿No es mucho más eficiente con la memoria simplemente pasar los valores de matriz de esas primitivas? ¿O el primer ejemplo de alguna manera sigue siendo solo O (1) memoria?

Y además de esta pregunta, me pregunto las diferencias de memoria de usar int [] e int especialmente para un tamaño de 1. Gracias de antemano. Simplemente me preguntaba si sería más eficiente con la memoria con Java y esto se me ocurrió.

¡Gracias por todas las respuestas! Ahora me pregunto rápidamente si debería "analizar" la memoria grande-oh de cada código, ¿se los consideraría O (1) o sería un error asumirlos?


Cuando pasa una matriz, el contenido de la matriz puede modificarse mediante el método que recibe la matriz. Cuando pasa prim primitiva, esas primitivas no pueden ser modificadas por el método que las recibe. Es por eso que a veces puede usar primitivas y, a veces, matrices.

También en general, en la programación de Java, se tiende a favorecer la legibilidad y permite que este tipo de optimizaciones de memoria se realicen mediante el compilador JIT.


En realidad, cuando pasa una matriz como parámetro a un método, se pasa una referencia a esta matriz bajo el capó. La matriz en sí se almacena en el montón. Y la referencia puede tener un tamaño de 4 u 8 bytes (dependiendo de la arquitectura de la CPU, la implementación de JVM, etc., aún más, JLS no dice nada sobre qué tan grande es una referencia en la memoria).

Por otro lado, el valor int primitivo siempre consume solo 4 bytes y reside en la pila.


La referencia de matriz int en realidad ocupa más espacio en los cuadros de pila que una primitiva int (8 bytes frente a 4). En realidad estás usando más espacio.

Pero creo que la razón principal por la que la gente prefiere la primera forma es porque es más clara y legible.

La gente realmente hace las cosas mucho más cerca del segundo cuando participan más ints.


Lo que te falta aquí: los valores int en tu ejemplo van en la stack , no en el montón.

Y es mucho menos costoso tratar con valores primitivos de tamaño fijo existentes en la pila, en comparación con los objetos en el montón.

En otras palabras: usar un "puntero" significa que debe crear un nuevo objeto en el montón. Todos los objetos viven en el montón; ¡no hay pila para arreglos! Y los objetos quedan sujetos a la recolección de basura inmediatamente después de dejar de usarlos. ¡Las pilas, por otro lado, van y vienen cuando invocas métodos!

Más allá de eso: tenga en cuenta que las abstracciones que nos brindan los lenguajes de programación se crean para ayudarnos a escribir código que sea fácil de leer, comprender y mantener. Su enfoque es básicamente hacer algún tipo de ajuste fino que conduzca a un código más complicado. Y esa no es la forma en que Java resuelve esos problemas.

Significado: ¡con Java, la verdadera "magia de rendimiento" ocurre en tiempo de ejecución, cuando el compilador just-in-time entra en acción! Verá, el JIT puede hacer llamadas en línea a métodos pequeños cuando el método se invoca "con la suficiente frecuencia". Y luego se vuelve aún más importante mantener los datos "cerca" juntos. Como en: cuando los datos viven en el montón, es posible que tenga que acceder a la memoria para obtener un valor. Mientras que los elementos que viven en la pila, aún pueden estar "cerca" (como en: en la memoria caché del procesador). Por lo tanto, su pequeña idea para optimizar el uso de la memoria podría ralentizar la ejecución del programa en órdenes de magnitud. Porque incluso hoy, hay órdenes de magnitud entre acceder al caché del procesador y leer la memoria principal.

Para resumir: evite entrar en tal "microajuste" para el rendimiento o el uso de la memoria: la JVM está optimizada para los casos de uso "normal, típico". Por lo tanto, sus intentos de introducir soluciones inteligentes pueden dar lugar fácilmente a resultados "menos buenos".

Entonces, cuando te preocupes por el rendimiento, haz lo que hacen los demás. Y si realmente te importa, entonces aprende cómo funciona la JVM. Resulta que incluso mi conocimiento está ligeramente desactualizado, ya que los comentarios implican que un JIT puede alinear objetos en la pila. En ese sentido: ¡concéntrese en escribir un código limpio y elegante que resuelva el problema de una manera directa!

Finalmente: esto está sujeto a cambios en algún momento. Hay ideas para introducir objetos de valor verdadero a java. Que básicamente viven en la pila, no en el montón. Pero no esperes que eso suceda antes de Java10. O 11. O ... (Creo que this sería relevante aquí).


Si estos métodos toman O (1) u O (N) depende del compilador. (Aquí N es el valor de i o i[0] , dependiendo). Si el compilador usa optimización de recursión de cola, entonces el espacio de pila para los parámetros, variables locales y dirección de retorno se puede reutilizar y la implementación será O ( 1) por el espacio. En ausencia de optimización de recursión de cola, la complejidad del espacio es la misma que la complejidad de tiempo, O (N).

Básicamente cantidades de optimización de recursión de cola (en este caso) para el compilador que reescribe su código como

public static int addUp(int i){ while(i != 0) i = i-1 ; return 0; }

o

public static int addUp(int[] i){ while(i[0] != 0) i[0] = i[0] - 1 ; return 0 ; }

Un buen optimizador podría optimizar aún más los bucles.

Hasta donde yo sé, ningún compilador de Java implementa la optimización de recursión de cola en la actualidad, pero no hay ninguna razón técnica que no se puede hacer en muchos casos.


Si su suposición es que los argumentos pasados ​​a funciones necesariamente consumen memoria (lo cual es falso por cierto), entonces en su segundo ejemplo que pasa una matriz, se realiza una copia de la referencia a la matriz. Esa referencia puede ser mayor que una int, es poco probable que sea más pequeña.


Varias cosas:

Lo primero será dividir pelos, pero cuando pasas un int en java estás asignando 4 bytes a la pila, y cuando pasas una matriz (porque es una referencia) en realidad estás asignando 8 bytes (asumiendo una arquitectura x64) en la pila, más los 4 bytes adicionales que almacenan el int en el montón.

Lo que es más importante, los datos que viven en la matriz se asignan al montón, mientras que la referencia a la matriz se asigna a la pila, cuando se pasa un entero no se requiere asignación de pila, la primitiva solo se asigna a la pila. Con el tiempo, la reducción de las asignaciones del montón significará que el recolector de basura tendrá menos cosas que limpiar. Mientras que la limpieza de los marcos de pila es trivial y no requiere procesamiento adicional.

Sin embargo, todo esto es discutible (imho) porque en la práctica, cuando tienes colecciones complicadas de variables y objetos, es probable que termines agrupando en una clase. En general, debe escribir para promover la legibilidad y el mantenimiento en lugar de tratar de exprimir hasta la última gota de rendimiento de la JVM. La JVM es bastante rápida, y siempre existe la Ley de Moore como barrera.

Sería difícil analizar el Big-O para cada uno de ellos porque para obtener una imagen real tendría que tener en cuenta el comportamiento del recolector de basura y ese comportamiento es altamente dependiente tanto de la propia JVM como de cualquier tiempo de ejecución (JIT) optimizaciones que la JVM ha hecho a su código.

Por favor, recuerden las sabias palabras de Donald Knuth de que "la optimización prematura es la raíz de todo mal"

Escriba un código que evite el micro ajuste, un código que promueva la legibilidad y la mantenibilidad le irá mejor a largo plazo.