Ajuste de desempenho Java: 10 técnicas comprovadas para maximizar a velocidade Java

Java Performance Tuning: 10 Proven Techniques to Maximize Java Speed

Improve the performance of your Java applications with effective tuning techniques. Discover strategies to optimize your code and increase overall efficiency.

Imagem em destaque

One of the most popular programming languages ​​with a wide variety of uses, Java has many useful qualities. However, it has often been criticized for its performance.

How do you resolve Java performance issues in your Java development services? Here, we'll explore several techniques for optimizing the performance of your applications, a process known as Java performance tuning.

#1 Profile for Performance Degradation

When optimizing your code, you should try to find the biggest performance issues and fix them first. But sometimes it's not obvious which part is causing the problems. In these cases, it is better to use a profiler.

A profiler is a tool used to identify performance issues such as bottlenecks, memory leaks, and other areas of inefficiency in code.

Java profilers work by collecting data about various aspects of a running Java application, such as the time taken to execute methods, memory allocations, thread behavior, and CPU usage. This data is then analyzed to provide detailed information about the application's performance characteristics.

Some commonly used Java profilers are VisualVM , JProfiler , and SeuKit . Intellij also provides a set of tools for application profiling .

#2 Performance Testing

Performance testing is the process of subjecting your application to realistic or extreme situations and analyzing its performance. Some popular options are Apache JMeter , Gatling , and BlazeMeter .

Profiling and performance testing are two different things. Creating profiles is similar to looking at a car closely and examining its different parts. On the other hand, performance testing is like taking a toy car for a spin and seeing how it performs in different situations. After you have performed performance tests and identified some errors, you can use a profiling tool to find the underlying cause of these problems.

#3 Load Testing

Load testing is a type of performance testing that involves simulating realistic loads on a system or application to measure its behavior under normal and peak load conditions.

Imagine you have a website and you want to know how many people can use it at the same time without it breaking or slowing down too much. To do this, we use special tools that simulate a large number of users using the website or application at the same time.

Some popular load testing applications are Apache JMeter, WebLoad LoadUI, LoadRunner NeoLoad, LoadNinja and so on.

#4 Use the Prepared Statement

In Java, Statement is used to execute SQL queries. However, the Statement class has some flaws. Let's look at the example below.

 Connection db_con = DriverManager.getConnection; 
Statement st = db_con.createStatement ;

 String username = "USER INPUT";
 String query = "SELECT user FROM users WHERE username="" + username + """;
 ResultSet result = st.executeQuery(query);

If the user passed ' OR 1=1– the resulting query would be:

 SELECT user FROM users WHERE username="" OR 1=1 -- '

This would cause the query to return all records from the users' table, as the OR 1=1 condition is always true. The double dash – at the end is an SQL comment, which causes the rest of the original query to be ignored. This type of attack is called SQL Injection.

Statements are vulnerable to SQL injections and therefore you must use PreparedStatement.

PreparedStatements are precompiled SQL statements. Being pre-compiled, they have much better performance than instructions that are compiled for each new query. Unlike declarations, they can be reused multiple times with different parameters. You can put "?" in the query that can be replaced with a desired parameter.

#5 Optimize Strings

As we know, strings in Java are objects. However, you may have noticed that strings behave a little differently than regular objects. Here is an example to demonstrate.

When we create two objects, they are given different memory locations and an equality comparison returns false.

 Object obj1 = new Object;
 Object obj2 = new Object;
 System.out.print(obj1 == obj2); // false

But for strings, comparing equality on two similar strings results in truth. In fact, this only occurs when we create strings using literal syntax. If you create strings using the constructor, the equality comparison will produce false.

 String str1 = "java";
 String str2 = "java";
 System.out.print(str1 == str2); // true

 String obj1 = new String("java");
 String obj2 = new String("java");
 System.out.print(str1 == str2); // false

This is because when strings are created using literal syntax, they are stored in a string pool, which is part of the heap memory. Whenever a new string is created, Java checks whether similar strings exist and, if so, returns the reference to the original string object instead of creating a new one.

Another thing to note is that strings are immutable and therefore string methods return new strings instead of modifying old ones. Therefore, every time you modify a string, you are creating new strings, each of which checks the string pool for duplicates. This is not optimized.

StringBuilder and StringBuffer

To solve these problems, Java provides the StringBuilder class, which is just a mutable sequence of characters. Use StringBuilder when modifying strings frequently.

StringBuffer works very similar to StringBuilder, except it can be synchronized across multiple threads.

Apache Commons StringUtils

The StringUtils class provides some additional methods for dealing with strings. Some of them perform better than native string methods. For example, StringUtils.replace is faster than String.replace . Additionally, most methods are null-safe, meaning they do not throw NullPointerException if the string is null.

Regular expressions

Use regular expressions when matching a pattern because they are much more efficient than any iterative pattern matching technique.

#6: Optimize the Java Virtual Machine Garbage Collection Process

The JVM is responsible for managing the execution environment of Java applications, including memory management, thread management, and garbage collection process. The default JVM settings are optimized for a wide range of applications and hardware configurations, but may not be ideal for specific applications and environments.

Choosing the Right Garbage Collector

The default collector for JDK 9 and higher is the G1 collector, which is designed to scale according to heap memory size. ZGC was developed for applications with large stacks that require low throughput.

-XX:+UseG1GC – to use the G1 collector

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC – to use ZGC

Adjusting heap memory size

The heap is the area of ​​memory where objects are allocated. By adjusting the heap memory size, you can optimize memory usage and avoid issues like OutOfMemoryError or excessive garbage collection.

To set the heap size, you can use the -Xmx and -Xms flags.

The -Xmx flag limits the maximum heap size that the JVM can allocate. For example, passing -Xmx2g will limit the maximum heap size to 2 gigabytes.

The -Xms flag sets the initial size of the heap. For example, if you set -Xms256m, the JVM will start with a heap size of 256 megabytes.

Adjusting some compiler options

There are many flags that can be used to customize the behavior of the Java Virtual Machine. Here are some of the most important ones:

-XX:MaxGCPauseMillis: This option specifies the maximum time that the garbage collector can pause the application.

-XX:UseAdaptiveSizePolicy: This option tells the JVM to dynamically adjust the size of young and incumbent generations based on the application's memory usage.

#7 Use recursion

Recursion is a great way to solve complex problems where the iterative solution may not be obvious. However, you should use recursion sparingly if memory usage is critical (e.g. embedded systems).

To understand why, let's look at how memory is allocated during a method call.

When a function is called, the JVM allocates a stack frame for the function on the call stack, which contains the function's local variables and method arguments. If the function calls another function, a new stack frame is allocated to that function and added to the top of the call stack.

In iterative approach, local variables are created once. However, in the case of the recursive approach, each stack frame has its own set of local variables, which may take up more space than necessary.

So if you're working in an environment where memory is limited, it's best to avoid recursion or add some kind of check that prevents recursion after a certain limit.

#8 Use of primitives and wrappers

Primitives are more efficient than their wrapper classes. This is expected because primitives only take up a fixed amount of space, while wrapper classes have their own local methods and variables that take up some extra space.

For similar reasons, try to avoid the BigInteger or BigDecimal classes if precision is not a concern.

However, there are times when you must use wrapper classes. For example, when using collections such as Lists and Maps, the Java Virtual Machine converts the primitives into their respective wrapper classes (autoboxing). In these cases, the use of primitives can lead to performance problems.

When creating wrapper class instances, try to use the static valueOf method instead of the constructor as it is deprecated since Java 9.

#9 Use the latest version of JDK

Unless your application depends on some features from older JDKs that have limited backwards compatibility with newer ones, there isn't much reason not to use the newer JDKs. Each new version comes with bug fixes, performance improvements and security patches. Newer versions may include features that improve your code in many ways.

#10 Premature optimization

Premature optimization refers to the practice of optimizing code, including using Java frameworks before it is necessary. There is no additional benefit to optimizing your code or selecting specific Java frameworks beforehand. It's better to focus first on making your code work and then worry about making it faster or selecting the ideal Java frameworks.

Focusing on optimization too early in the development process can divert valuable time and resources from more critical tasks such as functionality, reliability, and maintainability.

Instead of optimizing every part of the code, optimize the critical components or bottlenecks that have the greatest impact on performance. This approach allows you to get better results quickly.

Conclusion

In this Java performance tuning guide, we explore methods to increase the speed of your applications. This is crucial whether you are working in-house or outsourcing Java development. By adhering to these best practices and understanding the exact performance capabilities, you will be able to release higher quality products regardless of your development approach.

Common questions

How can Java developers efficiently manage memory usage to minimize the impact on application performance?

To manage memory, Java developers need to use the correct garbage collector. The G1 Garbage Collector is the most versatile. Second, you must discard unused objects by setting their reference to null.

What are some popular Java performance optimization tools and libraries to help developers improve application performance?

Some popular tools for Java performance monitoring are JMeter, VisualVM, JProfiler, and YourKit.

Additionally, Apache Commons are libraries that provide higher performance utility classes for common operations.

If you liked this, be sure to check out one of our other Java articles:

  • 7 Best Java Machine Learning Libraries
  • C# x Java: main differences explained
  • Java Concurrency: Master the Art of Multithreading
  • Java garbage collection
  • Java Integration Testing Explained with Examples

Source: BairesDev

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.