Optimize Java method using the Scopes concept

Asked

Viewed 627 times

23

Well some time ago, when I took some classes J2ME to Mobile (almost deceased ), where I was presented to a concept of scope hitherto unknown by me, that would be:

{
    // cria um novo escopo
}

Where anywhere a method is possible to force new scopes. Searching I found some sources and references that may be the article Thinking in Java on the website Linuxtopia and this other issue of Stackoverflow.

So seeking to optimize some features in my Android applications, I thought of refactoring my codes to take advantage of this form of resource management, only that I have no knowledge if it really can make a difference.

Then I present the following example, to contextualize my doubt:

Example:

Suppose I have one class large that allocates many resources while being initialized:

public class ClasseGrande{

    // ...

    public void alocaMuitosRecursos(){
        // Aqui inicia varios atributos da ClasseGrande()
        // ...
    }

    public String obtemResultado(){
        return "Resultado da classe grande";
    }    
}

And the following 3 approaches to use:

Approach 1 (My current):

// tudo no mesmo scope
public void testScopeMethod1(){
    ClasseGrande classeGrande = new ClasseGrande();
    classeGrande.alocaMuitosRecursos();

    // ... usar a classe grande

    String resultado = classeGrande.obtemResultado();

    // agora não se utiliza mais a classe grande só se manipula seu resultado

    // faz vários processos com o atributo resultado

    // ...

    // termina método, e só aqui todos os recursos são liberados para GC. Certo?
}

Approach 2 (using methods for dividing scopes):

// utiliza método para trocar de scope
public void testScopeMethod2(){
    String resultado = processaClasseGrande();

    // faz vários processos com o atributo resultado

    // ...

    // termina método, e libera os recursos do método testScopeMethod2
}

private String processaClasseGrande(){
    ClasseGrande classeGrande = new ClasseGrande();
    classeGrande.alocaMuitosRecursos();

    // ... usar a classe grande

    return classeGrande.obtemResultado();

    // aqui a classe grande já é liberada, pois finalizou-se seu escopo de método. Certo?
}

Approach 3 (using sub-scopes within the method itself, right after using the resource):

// com sub scope no método
public void testScopeMethod3(){
    String resultado;
    {
        ClasseGrande classeGrande = new ClasseGrande();
        classeGrande.alocaMuitosRecursos();

        // ... usar a classe grande

        resultado = classeGrande.obtemResultado();
        // aqui a classe grande já é liberada, pois finalizou-se seu escopo. Certo?
    }

    // agora não se utiliza mais a classe grande só se manipula seu resultado

    // faz vários processos com o atributo resultado

    // ...

    // termina método, e libera os recursos do método testScopeMethod, mas a classe grande já foi libera logo após ser utilizada. Certo?
}

Questions:

  • Does it really make a difference in performance? Why?
  • If it makes a difference, approaches 2 and 3 present differences of performance? Where and why?
  • If it makes any sense, it would be good practice to keep this in mind for robust designs, mainly thinking of devices mobile?
  • 2

    Fernando, could you check on Memory Monitor from Android Studio, along with a call to Garbage Collector, but if the processing done until the reference loss (in case 1) is small, there will be no difference in the scope change. You can also assign null to the reference, to try to release soon the large object (calling the GC later).

  • @Wakim, you say make explicit calls to the Garbage Collector (System.gc())? Because that my doubt would be not only for robust methods, but to always try to go this way, trying to minimize the resources to each part of the application, thus seeking a more efficient and optimized result, and the idea of scope seems to me a cleaner and more readable way to do this, compared to setar null and explicitly call the GC. But I do not rule out the possibility if it is to get better optimization of resources, because today I am facing serious problems of OutOfMemory on my app.

  • @Wakim, and regarding your tip to use Memory Monitor from Android Studio, I will try to create a test case here and see the results. Thanks for the tip. = D

  • Yes, it would be the call to System.gc, but it does not guarantee that it will run on time, it scalona an execution. OutOfMemory may be a sign of Memoryleaks, recently the Square released a tool that helps analyze their existence, called Leakcanary, maybe it’s worth analyzing if there’s no such problem in your app. Something else could be using Object Pools, to reduce the amount of objects created, take a look at this video from Colt Mcanlis to understand.

  • Actually using scopes is more "beautiful" than setting null and calling GC, but it would suit cases where you allocate large quantities, and at specific points, not by the whole code. Perhaps this is the case to use the largeHeap in his AndroidManifest.

  • 1

    @Wakim, I will review your tips especially regarding the Leakcanary, which from what I’ve seen can help identify what’s causing the Memoryleak in the application. And the largeHeap I am already using, my problems are punctual in some low standard devices, as some Genesis devices, for example. But I want to at least minimize these problems in these devices, which would guarantee me greater reliability in higher devices.

  • Fernando, if you still can’t solve the problem, I can give you one more suggestion. O Brisk, facebook library, can make memory allocation in special area of Android, where it has less size restriction... Isn’t it worth an attempt to allocate that large object there, manipulate and then dislocate? I’ve never used this area, but from what I’ve heard, you’re the one who manages allocation and displacement, so you need to be careful not to forget anything there...

  • @Wakim, solve not solved, but already improved some situations, the screens that cause problems Outofmemory on the devices mentioned, are the screens of Catalogo, where are screens with grids of images, I am already resizing the images to the appropriate size when loading it to memory, and I am already loading on demand and desalocando on demand, as the user descends and climbs on scroll from the page, but sporadically, it’s not always, Outofmemory happens, so the idea is to optimize everything else to try to keep more memory available for these screens.

  • 1

    @Fernando neither served nor forgot to accept one of them?

Show 4 more comments

3 answers

9


Something very important about scope is how long your objects will remain in the heap (Memory area where the JVM allocates objects).

It is not interesting that an object remains in memory beyond what is necessary, so it is important to define the scopes well because it facilitates the work of the Garbage Collector.

So, answering your questions:

Does it really make a difference in performance? Why?

Maybe. It is very important to make good use of computational resources, especially when we talk about cell phones and gatgets like.

Using the scope concept well is just a positive factor in your code, but that’s not all. Defining the scope well may not make your program faster, but it will keep you robust and consistent, unlike those programs that are getting slower and slower at the end of the day, forcing us to give reboots in it.

If it makes a difference, approaches 2 and 3 present differences of performance? Where and why?

Considering what I said in the first answer and analyzing the approaches, no. Looking quickly at the two I don’t see much difference between them.

Particularly I prefer the two approach as each object will exist during the execution of its respective method.

Approach 3 may be interesting in very large methods that allocate a good amount of objects, but even so I would consider a Refactoring.

If it makes any sense, it would be good practice to keep this in mind for robust designs, mainly thinking of devices mobile?

Yes. If you compare to a server, mobile devices are very limited. We need to always keep in mind that an app that consumes a lot of resources consumes a lot of battery, no one will use an app like this.

5

Don’t program in a JIT-friendly way, let him do the work

In a common hotspot, say, a widely used version in the industry, such as JVM 6, several features exist to allow the code you wrote to be optimized.

You write a file .java, it is compiled to .class and then runs inside a JVM that compiles at runtime to native code.

During this process, phenomena occur in your code, among them:

  • Inline Expasion - The method call is replaced by his body;
  • Reordering - Your code is reordered to run in the most effective way by the processor;
  • Escape Analyses - Defensive copies are not allocated to memory if the JVM realizes that they are not changed by client code (classes that have called methods), Synchronized methods (Synchronized) are called without the overhead traffic in the Shared Memory Bus the machine, if the JVM realizes that this method is not running concurrently (standard feature starting with Hotspot version 6u23).

These phenomena happen while running the program, which is being optimized based on its use, which is why in many Android devices, when restarting the system, it is slower. What no longer happens from version 5.0 that uses ART, or Android Runtime, which uses another technique, called A Head-of-time Compilation.

Do not use conium scopes/blocks for performance

Premature optimization will cause you problems design and you’ll have more verbose codes.

The fact that an object goes outside the scope of the method does not mean that it will be collected.

The object without strong references will only be collected in the next cycle of the Garbage Collector, which usually occurs in two phases - Major Collections and Minor Collections, in short it can remain in the heap even without reference.

I strongly recommend that you see this example: http://docs.oracle.com/cd/E15289_01/doc.40/e15058/underst_jit.htm#i1084566

Moral, devote yourself to design first, optimise then.

  • +1. In the particular case of example 1, many compilers analyze the life of variables (in English), although I don’t know if this is the case for JVM in particular, to draw this kind of conclusion.

  • Another trick is that how the function returns one String (and not e.g. a int), JVM may have difficulty proving that this string does not point to a shared member of ClasseGrande. If both the string and the ClasseGrande will be destroyed at the end of the method, OK. Otherwise, Garbage Collector will have to decide which members of the ClasseGrande it will have to destroy, i.e. the version with the scope gives plus (and not least) java work.

1

Approach 2 and 3 have no difference, in both you have divided the code into separate blocks to decrease the scope of objects.

On approach 1, if you still have a lot of processing to run below the line:

String resultado = classeGrande.obtemResultado();

You can divide it into different methods both for easy reading, while to be able to release the object to the 'Garbage Collector' (doesn’t mean it will be removed from memory, only that it is available).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.