style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Rumbo a nuestra Certificación (Tercera entrega)

Hola a todos los seguidores de Java, cómo les va?

Como ya se ha hecho costumbre seguimos con nuestros pequeños aportes para prepararnos en obtener la Certificaciòn de Expert Java Programmer.

Esta entrega contempla los siguientes temas:

a) Igualdad de objetos.
b) Operadores Lógicos.
c) Operador instanceof.
d) Operador condicional.
e) El recolector de basura (Garbage Collector).

Cualquier duda, comentario o aclaración por favor en el panel correspondiente.

Saludos y no caigamos en excepción alguna, je je je!

Igualdad de objetos.

Los operadores de igualdad (==) y desigualdad (!=) pueden utilizarse para comparar cualquier tipo compatible.

Ahora bien, si los utilizamos para comparar variables de tipo objeto debemos tener en cuenta que lo que contienen dichas variables son referencias a objetos, no los objetos en sí.

Esto implica que podemos tener dos variables referenciando a dos objetos iguales y que la condición de igualdad de las variables resulte falsa.

A continuación anexamos un ejemplo:

public class EjemploCompara {

        public static void main (String args[]) {

                String v1 = new String("hola");
                String v2 = new String("hola");

                if(v1 == v2)
                        System.out.println("Las dos variables apuntan al mismo objeto");
                else
                        System.out.println("Los objetos a comparar son diferentes");

        }

}

En el ejemplo anterior se aprecia que existen dos objetos de la clase String con el mismo valor de texto ("hola"), situados en zonas de memoria diferentes (referencias diferentes), por lo que al compararlos el programa indicará que no son iguales.

Para comparar la igualdad de objetos, las clases proporcionan un método llamado equals, en el que cada clase implementa su criterio de igualdad. Por lo tanto si queremos saber si dos objetos de una determinada clase son iguales se debe utilizar el método equals no el operador de comparación (==), el cual sólamente daría un resultado verdadero si las dos variables apuntan al mismo objeto.

Por último debemos tener en cuenta que el operador de igualdad (==) sólo puede comparar referencias del mismo tipo de objeto; si se comparan dos variables de tipos de objeto diferentes, se producirá un error de compilación. A continuación anexamos un ejemplo:

public class EjemploCompara002 {

        public static void main (String args[]) {

                Integer i = new Integer(25);
                String s = new String("hola");

                // La siguiente línea arroja el siguiente error de compilación
                // incomparable types: java.lang.Integer and java.lang.String
                if(i == s)
                        System.out.println("Los objetos a comparar son iguales");

        }

}

Operadores Lógicos.

Operan con los valores de tipo boolean, siendo el resultado también de tipo boolean.

A continuación se describe cada uno de ellos:

a) Operador lógico AND (&&): El resultado será true si los dos operandos son true, en cualquier otro caso el resultado será false.
b) Operador lógico OR (||): El resultado será true si alguno de los operandos es true.
c) Operador lógico NOT (!): Actúa sobre un único operando de tipo boolean, dando como resultado el valor contrario al que tenga el operando.

Cabe mencionar que los operadores AND (&&) y OR (||) funcionan en modo "cortocircuito", esto significa que si el primer operando determina el resultado de la operación, el segundo operando no será evaluado.

A continuación anexamos un ejemplo:

public class EjemploCorto {

        public static void main (String args[]) {

                int p = 4, f = 2;

                if((p > 0) || (++f > 0)) {
                        p++;
                }

                System.out.println("La variable p vale: " + p);
                System.out.println("La variable f vale: " + f);

        }

}

Al ejecutarse dicho código se imprimirá en pantalla lo siguiente:

La variable p vale: 5
La variable f vale: 2

Lo que demuestra que la expresión incluida en el segundo operando de OR (||) no llega a ejecutarse pues, al ser true el primer operando (p > 0), el resultado de toda la expresión será directamente true.

Operador instanceof.

Opera únicamente con referencias a objetos y se utiliza para saber si un objeto pertenece a determinado tipo. La forma de utilizarlo es la siguiente:

referencia_objeto instanceof clase

El resultado de la operación será true si el objeto pertenece a la clase especificada o a una de sus subclases.

A continuación anexamos un ejemplo:

public class EjemploInstance {

        public static void main (String args[]) {

                String s = "Hola";

                if(s instanceof String) {
                        System.out.println("El objeto es una cadena");
                } else {
                        System.out.println("El objeto no es una cadena");
                }

        }

}

Al ejecutarse dicho programa mostrará en pantalla: El objeto es una cadena.

Operador condicional.

Se trata de un operador ternario (consta de tres operadores) cuya función es asignar un valor entre dos posibles a una variable, en función del cumplimiento o no de una condición. Su formato es el siguiente:

tipo variable = (condicion)?valor_si_true:valor_si_false

Si la condición resulta verdadera (el resultado es true), se almacenará en la variable el resultado de la expresión valor_si_true, si no, se almacenará valor_si_false.

A continuación anexamos un ejemplo:

public class EjemploCondicional {

        public static void main (String args[]) {

                int k = 5;

                String s = (k%2 == 0)?" es par ":" es impar";
                System.out.println("El número " + k + s);

        }

}

El recolector de basura de Java.

El recolector de basura (Garbage Collector) es una aplicación que forma parte de la JVM (Java Virtual Machine) y cuyo objetivo es liberar de la memoria los objetos no referenciados. Cuando un objeto deja de estar referenciado, se le marca como "basura", a partir de entonces la memoria que ocupa puede ser liberada por el recolector.

La desición de en que momento se va a ejecutar el recolector de basura depende de la implementación de la máquina virtual, por lo tanto, en Java no es posible saber en qué momento exacto un objeto será destruido, sólo es posible determinar el momento en que esto puede suceder (cuando se elimine la última referencia al mismo).

Si un objeto va a dejar de ser utilizado en un programa, conviene eliminar las referencias al mismo para que sea marcado como "basura". Esto se puede hacer asignando el valor "null" a la variable o variables que apunten al objeto.

Integer i = new Integer(30); // Se crea el objeto
....
....
....
....
i = null; // Pérdida de la referencia, objeto candidato para el recolector de basura.

Aunque no asignemos el valor de "null" a la variable que apunta al objeto, en el momento en que ésta salga de ámbito, se perderá la referencia y el objeto será etiquetado como "basura".

---------------------------------------------------
Miércoles 13 de mayo del 2009.
Jhanno Foulds Gaunt - Km3.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

gracias por el aporte

Gracias por el aporte hay cosas simples que a veces se olvidan o se pasa por alto pero siempre conviene recordarlas

Incorrección

Por favor eliminen este párrafo:

Si un objeto va a dejar de ser utilizado en un programa, conviene eliminar las referencias al mismo para que sea marcado como "basura". Esto se puede hacer asignando el valor "null" a la variable o variables que apunten al objeto.

Esto no es conveniente, es redundante, ¿dónde lo leyeron?

Javier Castañón

Imagen de iberck

(J2ME) Conveniente, no redudante, práctico

Por favor eliminen este párrafo:

"Si un objeto va a dejar de ser utilizado en un programa, conviene eliminar las referencias al mismo para que sea marcado como "basura". Esto se puede hacer asignando el valor "null" a la variable o variables que apunten al objeto"

Esto no es conveniente, es redundante, ¿dónde lo leyeron?

Javier Castañón

Beginning J2ME, From Novice to Professional.

Be Clean
One simple piece of advice is to clean up after yourself. Releasing resources as
soon as you are done with them can improve the performance of your application.
If you have internal arrays or data structures, you should free them when
you’re not using them. One way to do this is to set your array reference to null so
the array can be garbage collected. You could even call the garbage collector
explicitly with System.gc() if you’re anxious to release memory back to the runtime
system.

En contextos limitados como J2ME, es práctico, conveniente y no es redudante marcar referencias nulas.
Una forma de ayudarle al garbage collector a encontrar la "basura" en dispositivos con capacidades limitadas es marcando referencias nulas para evitarle el trabajo de buscar referencias perdidas.
Para J2SE no tiene sentido práctico pero es importante saberlo ya que el objetivo de la certificación es conocer un poco más del lenguaje (Como tip para SCJP, estudien la forma en que se entrelazan las referencias y el garbage collector las barre)


https://nbtapestrysupport.dev.java.net/

Imagen de Jhanno

Muchas gracias por el

Muchas gracias por el aporte. Debo indicar y reiterar que todo el texto y ejemplos que publicamos están probados y revisados con 3 diferentes materiales para la Certificación. Nos esforzamos por dar a los novatos el mejor material Web para su certificación. Cualquier duda estamos a sus órdenes. Saludos cordiales.

Permítaseme disentir y explicar

Nadie discute el objetivo de aprendizaje, que para el contexto de la certificación es muy claro: cuando un objeto ya no tiene referencias hacia él, es candidato para ser recogido por el colector de basura. Eso es muy distinto a sugerir que es buena práctica nulificar las referencias a los objetos cuando dejan de ser utilizados, salvo contadas excepciones.

Abriré un post de blog para explicar por qué en general es redundante nulificar las referencias por defecto, basado en los escritos de dos de los múltiples autores del JDK, a saber: James Gosling y Joshua Bloch publicados nada más ni nada menos que por Sun. A partir de ahí se comprenderá:

1) Por qué las convenciones de codificación de Sun no recomiendan nulificar las referencias
2) Por qué el código fuente del JDK no nulifica las referencias. (No me crean, ¡compruébenlo!)
3) Por qué en general es mala idea invocar System.gc()
4) Por qué el colector de basura puede recoger objetos que aún tengan referencias hacia ellos.
5) Por qué el colector de basura puede recoger objetos aún antes de ser anuladas sus referencias.
6) Cuándo sí es deseable nulificar referencias para prevenir goteos de memoria.

Como un adelanto, voy a transcribir un fragmento de un librito llamado "Tha Java Programming Language, 4th Edition" escrito por Ken Arnold, James Gosling y David Holmes

17.5.4. Finalization and Reachability

An object becomes finalizable when it becomes weakly reachable (or less). It would seem that to determine reachability you need simply examine the source code for your applications. Unfortunately, such an examination would often be wrong. Reachability is not determined by the statements in your source code, but by the actual state of the virtual machine at runtime. The virtual machine can perform certain optimizations that make an object unreachable much sooner than a simple examination of the source code would indicate.

For example, suppose a method creates an object that controls an external resource and that the only reference is through a local variable. The method then uses the external resource and finally nulls the local reference or simply returns, allowing the object to be finalized and release the external resource. The programmer might assume that the object is reachable until that reference is set to null or the method returns. But the virtual machine can detect that the object is not referenced within the method and may deem it unreachable and finalizable the instant after it was constructed, causing immediate finalization and release of the resource, which was not something the programmer intended.

Ahora transcribiré otro pedacito de un librito llamado "Effective Java Programming, 2nd Edition" escrito por un señor llamado Joshua Bloch (las negritas no son mías, son del señor Bloch):

Item 6: Eliminate obsolete object references

An added benefit of nulling out obsolete references is that, if they are subsequently dereferenced by mistake, the program will immediately fail with a NullPointerException, rather than quietly doing the wrong thing. It is always beneficial to detect programming errors as quickly as possible.

When programmers are first stung by this problem, they may overcompensate by nulling out every object reference as soon as the program is finished using it. This is neither necessary nor desirable, as it clutters up the program unnecessarily. Nulling out object references should be the exception rather than the norm. The best way to eliminate an obsolete reference is to let the variable that contained the reference fall out of scope. This occurs naturally if you define each variable in the narrowest possible scope.

Debo aclarar que todo esto es material avanzado, que la certificación no requiere que sepan toda esta arcana, pero me parece importante señalarles que aún los libros contienen errores, por eso pregunté "¿Dónde leyeron esto?" Por cierto, consulté también el libro de certificación de Kathy Sierra, y no hace la recomendación de nulificar.

Debo decir también que antes de hacer esta entrada de blog, requiero hacer otra explicando cómo utilizar JPA y Hibernate. Espero comprenderán que el tiempo es limitado :-O

Saludos cordiales

Javier

Edición: Joshua Bloch establece también la necesidad de nulificar las referencias a objetos que representan estructuras de datos, como el autor del libro J2ME. En este último caso, no está recomendando nulificar todas las referencias a objetos. Por último, preguntaría: si "es práctico, conveniente y no es redudante marcar referencias nulas" ¿por qué no es una práctica que sigan los programadores del JDK, Eclipse, etc.? (ojo, en el caso de Eclipse no cuentan dereferenciar el código SWT, pues no es Java puro, sino un híbrido con C).

Permíteme insistir

¿En cuál material dice que se "conviene eliminar las referencias al mismo para que sea marcado como "basura"? Tal vez se trate sólo de un error de traducción. Notas:

1) Nadie está discutiendo que nulificar la única referencia a un objeto sea una estrategia válida para marcarlo como candidato a ser recogido por el colector de basura.
2) La referencia al libro de J2ME establece un contexto muy claro para nulificar: asignar cualquier estructura de datos a null previene goteos de memoria. No sugiere que se haga para todas las variables.

Saludos

Javier

Imagen de Jhanno

Clarificando los objetivos de la Certificación

Agradecemos todos los aportes que se han suscitado de un tema tan especial como el "Garbage Collector", a continuación y para efecto de la comprensión para los novatos, citaré un fragmento del Libro "Piensa en Java".

La recolección de basura no es destrucción.

Si se recuerda esto, se evitarán los problemas. Lo que significa que si hay alguna actividad que debe llevarse a cabo antes de que un objeto deje de ser necesario, hay que llevar a cabo esa actividad por uno mismo. (Bruce Eckel).

Recomiendo el capítulo denominado: "Inicialización y limpieza" de "Piensa en Java" para aunar en el tema.

Saludos cordiales.

Imagen de iberck

2) La referencia al libro

2) La referencia al libro de J2ME establece un contexto muy claro para nulificar: asignar cualquier estructura de datos a null previene goteos de memoria. No sugiere que se haga para todas las variables.

Nadie sugirió que se hiciera para todas las variables

Abriré un post de blog para explicar por qué en general es redundante nulificar las referencias por defecto

Sería un buen aporte, así las personas que estudian para la certificación conocerán más del lenguaje y buenas prácticas de programación en JSE, JEE.

Por último, preguntaría: si "es práctico, conveniente y no es redudante marcar referencias nulas" ¿por qué no es una práctica que sigan los programadores del JDK, Eclipse, etc.? (ojo, en el caso de Eclipse no cuentan dereferenciar el código SWT, pues no es Java puro, sino un híbrido con C).

Por que te falto incluir toda la frase:

En contextos limitados como J2ME, es práctico, conveniente y no es redudante marcar referencias nulas.

http://nbtapestrysupport.dev.java.net/

Buen punto

Antes de que el colector de basura reclame la memoria de un objeto, éste debe estar eun estado "finalizable", y antes de eso, el objeto debe ser "alcanzable". Imaginemos un televisor y tres controles remotos (mandos a distancia). El televisor está prendido y es el objeto y los controles remotos las referencias (o variables). Hay una persona que debe llevarse el televisor, pero no lo puede hacer mientras haya un control remoto activo y el televisor esté encendido.

A cada control remoto esperamos que se le acaben las pilas (baterías) o se las quitamos (quedan fuera de alcance o las nulificamos, respectivamente). Cuando la persona que debe llevarse el televisor verifica que ya nadie puede enviarle señales al televisor, entonces significa que el aparato es inalcanzable para los demás pero alcanzable para él. En ese momento lo apaga (lo finaliza, es decir invoca el método finalize()) y a continuación se lo lleva (reclama la memoria).

Los objetos escritos en C++, tienen unos métodos especiales llamados destructores, que deben ser invocados manualmente y que sirven para liberar recursos y liberar el espacio en memoria. En Java no hay destructores, y si hay que liberar un recurso como una conexión a una base de datos o un "handle" de un archivo, debe hacerse manualmente tan pronto como se pueda, y no poner este código en el método finalize() pues no se sabe cuánto tiempo pasará para que el colector de basura haga su trabajo, aún si lo invocamos con System.gc(), pues simplemente se puede negar a obedecernos. Su comportamiento no es determinístico, es más bien probabilístico. Luego entonces, la recolección de basura en Java no es como la destrucción de objetos en C++.

Saludos

Javier

No importa que sea J2ME

Veamos el problema con la redacción, dado que está diriga a novatos:

Si un objeto va a dejar de ser utilizado en un programa, conviene eliminar las referencias al mismo para que sea marcado como "basura". Esto se puede hacer asignando el valor "null" a la variable o variables que apunten al objeto.

Entonces, en mi código hago lo que me me dicen que es conveniente:

class Foo {
void Bar() {
        Integer i = new Integer("1");
        Date d = new Date("1");
        String s = "dummy";
        //
        // Más código
        //
        i = null;
        d = null;
        s = null;
    }
}

Esto es innecesario y redundante en J2ME o en J2SE. ¿Me explico? El autor del libro que citas es claro respecto a nulificar referencias a estructuras de datos, ello para evitar goteos de memoria. ¿Cuál puede ser una estrategia agresiva en J2ME? Una que aplique tal vez a variables miembro. Cito:

How can I make it easy to ensure an object's eligibility for garbage collection?
- Simple, maintain as few as possible references to the object. A good approach is to use the singleton pattern for having a "reference manager". Since only it maintains single references to all objects only one reference need be nullified.
- Another approach that a member of these forums pointed out was to kill the thread that has only its own local references to objects. Once the thread is killed the references die with it, making the objects elegible for garbage collection.

Fuente:
http://www.j2meforums.com/wiki/index.php/FAQ#Memory_Management

Aún en dicho caso, ambas estrategias son aplicables a J2ME y J2SE

Entonces mi propuesta de redacción es la siguiente:

Para que un objeto sea marcado como "basura" no deben existir referencias al mismo. Una manera de eliminar las referencias es asignando el valor "null" a la variable o variables que apunten al objeto. Aunque no asignemos el valor de null a la variable que apunta al objeto, en el momento en que ésta salga de ámbito, se perderá la referencia y el objeto será etiquetado como "basura".

Saludos

Javier Castañón

Edición: Nótese que también estoy sugiriendo juntar dos párrafos en uno solo, para hacerlo aún más explícito.

Imagen de iberck

Bien

Hola Javier:

Para que un objeto sea marcado como "basura" no deben existir referencias al mismo. Una manera de eliminar las referencias es asignando el valor "null" a la variable o variables que apunten al objeto. Aunque no asignemos el valor de null a la variable que apunta al objeto, en el momento en que ésta salga de ámbito, se perderá la referencia y el objeto será etiquetado como "basura".

Coincido con tu propuesta de redacción para no confundir a la gente que estudia para la certificación

Mi propuesta de redacción es la misma, solo agregaría 3 palabras para que no se mal entienda:

En contextos limitados como J2ME, es práctico, conveniente y no es redudante marcar referencias nulas bajo ciertas circunstancias

De hecho en j2me se rompe con muchas best practice, por citar un ejemplo se recomienda NO utilizar la encapsulación bajo ciertas circunstancias, pero ya nos estamos metiendo en otros temas..

Un saludo

http://nbtapestrysupport.dev.java.net/

El caso redundante ya quedó ejemplificado

Y me parece que es explícito en cuanto a lo que considero un caso redundante e inconveniente. Pero creo que debemos ser explícitos respecto a cuáles son "ciertas circunstancias", para que un novato las pueda distinguir.

Yo tomaría dos heurísticas más allá de lo que es indispensable saber para certificarse (hay gente certificada que no comprende adecuadamente las causas de los goteos de memoria, por ejemplo):

- Las estructuras de datos y sus miembros hay que vigilar que sean adecuadamente nulificadas según corresponda.
- Hay que cuidarse de aquellas referencias que son externas a nuestra instancia.

Voy a ejemplificar utilizando casos de "goteos de memoria" Un goteo de memoria se da cuando una aplicación no libera la memoria consumida y que ya no usa, y conforme pasa el tiempo consume más y más memoria.

Un ejemplo del libro de Joshua Bloch, crea una pila con goteo de memoria. Se asume que el lector conoce cómo funciona una pila.

// Can you spot the "memory leak"?
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

    /**
     * Ensure space for at least one more element, roughly
     * doubling the capacity each time the array needs to grow.
     */

    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

El pecado está en el método pop()

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; // Eliminate obsolete reference
    return result;
}

Al no nulificar la referencia a un elemento de la matriz cuando éste ya no será utilizado (al menos no por nuestra instancia de Stack), la referencia aunque salga de ámbito, no será reclamada por el colector de basura, pues nosotros guardamos inadvertidamente una referencia a ella en nuestra matriz. Entonces o nulificamos el elemento o nulificamos la matriz completa para evitar el goteo de memoria.

Ya que mencionaste el encapsulamiento, se me ocurrió un ejemplo donde el adecuado encapsulamiento reduce el riesgo de gotear memoria. Al igual que el ejemplo anterior, aplica en cualquier edición de Java, pero en este caso, el problema no se resolverá nulificando referencias.

class Foo {
    Date d;
    void bar(Date d) {
        this.d = d;
    }
}

Considerando una instancia de una clase Bar, que pasa una referencia de Date a una instancia de Foo, ésta última no podrá morir hasta que se muera la insancia de Bar. ¿La razón? No se está encapsulando/ocultando la información adecuadamente: Bar tiene una referencia a una instancia de Date que pasó a una instancia de Foo. Aunque Foo se quera morir, tiene que esperar que Bar muera o elimine su referencia a la instancia de Date, y viceversa:

Bar -> (Date d) <- Foo

A continuación un código que con encapsulamiento evita posibles goteos de memoria:

class Foo {
    Date d;
    void bar(Date d) {
        this.d = (Date) d.clone();
    }
}

En este caso se codificó el método bar() de una manera defensiva: se obtiene una copia local de la instancia de Date. Discutiblemente, ocupa más memoria porque crea un objeto adicional a si sólo se pasara la referencia, pero dado que la instancia de Foo ya no está conectada con la instancia de Bar, el colector de basura puede reclamar a cualquiera de los dos por separado: podría ocurrir que gracias a que se permite al colector de basura haga su trabajo, termine ocupando menos memoria al paso del tiempo.

Saludos

Javier Castañón

Imagen de iberck

Ejemplo práctico

Y me parece que es explícito en cuanto a lo que considero un caso redundante e inconveniente. Pero creo que debemos ser explícitos respecto a cuáles son "ciertas circunstancias", para que un novato las pueda distinguir.

El ejemplo que pones de goteos de memoria no creo que le sirva de forma práctica a un novato, pero me parece acertado de tu parte dar un ejemplo explicito para que los programadores que lean esto sepan que no todo lo que brilla es oro y una mala programación puede llevar a fugas de memoria.

De hecho en j2me se rompe con muchas best practice, por citar un ejemplo se recomienda NO utilizar la encapsulación bajo ciertas circunstancias, pero ya nos estamos metiendo en otros temas.

Aclaro, romper con la encapsulación en dispositivos con capacidades limitadas sirve para mejorar el performance y optimizar el tamaño de la aplicación cuando no es suficiente con una obfuscación.

En el micro mundo, los programadores que piensan que tiene terminada una aplicación cuando les corre en un emulador, viven en el jardín del edén ya que no llevan ni el 60% de la tunda que les espera en el mundo real. En dispositivos con capacidades limitadas se debe romper y olvidar muchas las best practices de la programación standar o empresarial, en ocaciones debes bajar a un nivel de abstración tal que los operadores a nivel de bit son el pan de cada día para ciertas operaciones aritméticas y empaquetado de información.
Y ya que mencionas el stack, en J2ME se recomienda pasar en medida de lo posible pocos parámetros en métodos que son llamados frecuentemente, si se requiere pasar una buena cantidad de variables es mejor declararlas a nivel de clase, es decir en el heap (es muy reducido, pero que muy reducido su tamaño)

Para enriquecer la plática, voy a poner un caso práctico de dónde marcar referencias nulas en dispositivos con capacidades limitadas ya para terminar con esto por que se está haciendo muy largo, pero antes del caso práctico es importante resaltar los siguientes puntos:

  • El algoritmo que utiliza la KVM para liberar y reservar objetos en memoria es distinto al de la VM, aqui se marca y se limpia. En cuanto un objeto deja de utilizarse y es candidato para el recolector de basura, su pedazo de memoria se marca como libre y puede ser utilizada por la KVM para colocar otra referencia. El problema de este algoritmo es que no relaciona espacios de memoria libres, aunque sean contiguos, y por lo tanto produce una gran fragmentación de la memoria.
  • En la configuración CLDC para la mayoría de los dispositivos móviles no está implementado el método finalize() por tanto, entre otras cosas, no se garantiza que al hacer la recolección de objetos lance peticiones para que se liberen sus recursos nativos, lo cual se ve ejemplificado en la mayoría de los celulares Nokia donde al crear una conexión HTTP(S) no cierra las conexiones EDGE/GPRS de forma nativa aunque se cierre ó iguale a null todos los flujos/conexiones, incluso se queda abierta sí se envía Connection:Close en las cabeceras HTTP. ¿Bonito verdad?, empiezan a gustarme los estandares :P

El siguiente es un trozo de código tomando de Beginning J2ME from Novice to Professional para realizar una conexión por bluetooth/obex, tomen en cuenta que se está en un contexto multithread:

public class OBEXMIDlet
extends MIDlet
implements CommandListener, Runnable {    
...

public void run() {
        try {
            //      System.out.println("b4 svr gcf open...");          
            mServerNotifier = (SessionNotifier) Connector.open(url);
            //      System.out.println("after svr gcf open...");
        } catch (Exception e) {
            System.err.println("Can't initialize OBEX server: " + e);
        }
       
        while (!mEndNow) {
            mConnection = null;
           
            try {
                mConnection = mServerNotifier.acceptAndOpen(mOpHandler);
            } catch (IOException e) {
                continue;
            }
           
         
            //          System.out.println(" got a connection!");
            try {
                // bad bad API design, need to synchronize server thread
                synchronized(this) {
                    this.wait();
                }
               
                //       System.out.println("svr: before conn close");
                mConnection.close();
            } catch (Exception ex) {
                // log exception
            }
           
        }  // of while
        try {
            mServerNotifier.close();
        } catch (Exception ex) {
            System.out.println("trying to close session...exception");
            ex.printStackTrace();
        }
    }
...
}

Lo importante está aquí:

while (!mEndNow) {
            mConnection = null;
...
}

El ciclo terminará de la mejor forma cuando mEndNow=true, tan pronto marquemos mConnection = null será elegible por el garbage collector incluso aunque no haya salido del ciclo while.

Espero depués de todo esto los programadores que lean este post tengan una idea más clara de dónde marcar referencias nulas y bajo que contextos aplicarlo.
Un saludo

http://nbtapestrysupport.dev.java.net/

Ejemplo práctico

iberck:

El ejemplo que pones de goteos de memoria no creo que le sirva de forma práctica a un novato

Javier:

Yo tomaría dos heurísticas más allá de lo que es indispensable saber para certificarse (hay gente certificada que no comprende adecuadamente las causas de los goteos de memoria, por ejemplo):

Creí que había dejado en claro el contexto, su relación con la certificación y sus consecuencias prácticas.

Pasando a otra cosa, sería bueno agregar un if para que no se ocasione un NullPointerException ¿no?

while (!mEndNow) {
            mConnection = null;
           
            try {
                mConnection = mServerNotifier.acceptAndOpen(mOpHandler);
            } catch (IOException e) {
                continue;
            }

Por último, reitero que mis observaciones han estado orientadas hacia la precisión técnica y prácticas comúnmente seguidas en la plataforma Java Standard Edition (no acostumbro ni me atrevo a llamarlas mejores prácticas), que es a la que va dirigida la certificación SCJP. De ninguna manera la intención ha sido irritar a nadie, y sinceramente espero que en el futuro podamos tener nuevas y más discusiones técnicas en español, en lo particular he disfrutado mucho este intercambio que considero enriquecedor.

Afectuosamente

Javier Castañón

... Y todo se resume al contexto...

Creo que ya se había comentado anteriormente (y con mucho énfasis) que el hacer asignación al valor null es necesario/permitido en contextos muy específicos de acuerdo al problema que se esté intentando solucionar. Ambos, los últimos ejemplos, citan claras aplicaciones y excepciones a dicha regla. Mi punto es el de notificar que el hilo del tema principal se va desviando cada vez un poco más, aunque con interesantes citas y aclaraciones, podría confundir realmente a principiantes, y no es algo en lo que tengan que preocuparse inicialmente del todo. Ah, y qué yo intentaré desviarlo incluso más con lo siguiente:

El hecho de citar textos no implica que esté libre de errores o que el autor no esté violando alguna regla de "mejor práctica" en el intento, a fin de cuentas, el tópico abordado por el autor está obviamente sujeto a su experiencia y en muchas ocasiones, los autores al redactar ejemplos se brincan muchas de estas situaciones por "claridad" o por ahorrar incluso espacio. Obviamente lo anterior es dependiendo de quién redacta el libro y que tan "experto" es del tema (por aquello de citar a Joshua Bloch y otras deidades).
Habiendo dicho esto, incluso en el ejemplo del MIDlet hay cuestiones que me hacen pensar que no por ser un programa que va a ser utilizado por un dispositivo con limitados recursos y con el propósito de "enseñar", puedes violar reglas muy sencillas al desarrollar una aplicación concurrente. Por ejemplo, falta de uso de try-finally para garantizar que realmente los recursos como conexiones están cerrados. El uso de volatile a variables de estado (mEndNow) que garantize visibilidad de memoria adecuada ante la presencia de múltiples contextos de ejecución (aquí citaría a Brian Goetz). Y por último... a mí no se me parece que el autor hace la asignación de null a mConnection por la cuestión de permitirle al garbage collector disponer de ella antes; tan sencillo como revisar al salir del ciclo que el recurso esté cerrado, y si no lo está, simplemente hacerlo. Como con las conexiones en JDBC, el que un objeto (Connection, Statement, ....) pueda ser elegible para recolección de basura no implica que el recurso debajo de él pueda ser cerrado/limpiado correctamente, es decir.. si olvidas invocar close() el gc no garantiza (ni el driver) que cualquier recurso que tu objeto estaba utilizado va a ser cerrado.

Otro punto digno de citar, ya que mencionamos a Joshua Bloch es que... incluso con las APIs hay errores/deficiencias, y Java tiene los suyos como lenguaje de programación e incluso con la API estándar(qué sería otro tópico distinto), a lo cual Joshua Bloch cita frecuentemente y en negritas en Effective Java Programming. También los diseñadores de APIs tienen resbalones.

Amén.

Imagen de iberck

Participen en los foros

Javier:

Pasando a otra cosa, sería bueno agregar un if para que no se ocasione un NullPointerException ¿no?

Por simplicidad tomé el código tal cual del libro, dejemos que alguien más participe en este post y responda dónde y por que agregar la sentencia if para no causar un NPE

Javier:

Creí que había dejado en claro el contexto, su relación con la certificación y sus consecuencias prácticas.

Tienes razón, simplemente no se me hizo un ejemplo práctico y por lo mismo expuse uno.

Javier:

De ninguna manera la intención ha sido irritar a nadie, y sinceramente espero que en el futuro podamos tener nuevas y más discusiones técnicas en español, en lo particular he disfrutado mucho este intercambio que considero enriquecedor.

Ni la mia, me da gusto compartir con gente como tú. La forma en la que has expuesto el tema ha sido técnicamente impecable y cada quien desde un enfoque distinto dio su punto de vista. De igual manera invito a que todos participen activamente en los foros y blogs ya que es otra manera de aprender.

Un saludo

http://nbtapestrysupport.dev.java.net/

Imagen de iberck

Último post

En la primer parte preguntaste y respondiste tú mismo.

amnesiac:

Habiendo dicho esto, incluso en el ejemplo del MIDlet hay cuestiones que me hacen pensar que no por ser un programa que va a ser utilizado por un dispositivo con limitados recursos y con el propósito de "enseñar", puedes violar reglas muy sencillas al desarrollar una aplicación concurrente. Por ejemplo, falta de uso de try-finally para garantizar que realmente los recursos como conexiones están cerrados

amnesiac:

y en muchas ocasiones, los autores al redactar ejemplos se brincan muchas de estas situaciones por "claridad" o por ahorrar incluso espacio

amnesiac:

El uso de volatile a variables de estado (mEndNow) que garantize visibilidad de memoria adecuada ante la presencia de múltiples contextos de ejecución (aquí citaría a Brian Goetz)

mEndNow no necesita ser volatile ni estar sincronizada por que su valor no es modificado en el ciclo de vida de los threads, el único que la toca es el método destroyApp() al finalizar el MID.

amnesiac:

tan sencillo como revisar al salir del ciclo que el recurso esté cerrado, y si no lo está, simplemente hacerlo

Y si se termina la memoria/recursos antes de salir del ciclo ? Recuerda estamos en un contexto limitado donde cada recurso es oro molido.

Otro punto digno de citar, ya que mencionamos a Joshua Bloch es que... incluso con las APIs hay errores/deficiencias, y Java tiene los suyos como lenguaje de programación e incluso con la API estándar(qué sería otro tópico distinto), a lo cual Joshua Bloch cita frecuentemente y en negritas en Effective Java Programming. También los diseñadores de APIs tienen resbalones.

Totalmente de acuerdo, ningún programador es perfecto y ningún sistema libre de bugs.

Un saludo
http://nbtapestrysupport.dev.java.net/

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">