Enhance the performance of the Java program

zhaozj2021-02-11  217

Over time, Java virtual machines become get better and better, but through some simple skills, you can still significantly improve the performance of the program.

Introduction

Many of Java have been widely praised. In particular, "once programming, run everywhere" enables developers to freely carry out the development of cross-platform applications without the expenditure of pre-processor instructions. It is usually considered that the weakness of Java is its performance.

In the current understanding is not completely correct, there are many products to improve the performance of the Java program and make it no longer a problem in many applications. For example, TowerJ is a later compiler that converts Java byte code into a highly optimized local executable program, JROCKIT is a Java virtual machine with an adaptive optimization capabilities. Despite this, use some simple techniques to make you don't have to buy these tools described above can also improve the performance of Java code. I will explain some of them in text.

The discussion of this paper is mainly based on those high throughput code (server-side). Given that the main overhead is caused by the GUI code involved in object creation and recreating, the performance estimation pointer to the server-side code is the execution time of the method. Therefore, for the sample code involved, I record the average time required to perform the method. Recording an accurate execution time of a method is not practical, so I timed on a series of methods and calculated their average. This effectively simulates the execution of performance-critical code.

Each example has pseudo code that explains byte code operations. The actual byte code produced can be obtained from the CUJ's Web site (www.cuj.com/code). Interpretation of all byte codes can be obtained from JavaSoft's site.

Improve the performance of string processing

Like C , the Java library defines its own String type. Under its appearance, this type is implemented by a CHAR type array, however, using a string does not need to be understood. NULL ('/ 0') is a scourge that causes many students who have been frustrated and used in the process of c ; using Java does not have to be distracting, programmers can focus on the application itself and the tools used to create applications. However, there is an unfavorable aspect associated with this worry-hearted string processing, that is, the connection operator ' ' of the string.

This operator looks very useful. Most of the applications that need to be written to the stream are used to use ' '. E.g:

String name = new String ("joe");

System.out.println (Name "is my name.");

In the code segment above, it seems that it seems that it cannot make any modifications in the println statement to improve their execution. However, the byte code generated by this statement (herein is expressed in this pseudo code), revealing the fact, see the program list 1.

Listing 1: Pseudo code Create New String (STR_1) generated by the byte code operation generated by the string connective ' '

Duplicate the string

Load the constant string "joe" (STR_2)

Call string constructor

Store this string in the local variable arrival position 0

Get the static outfild from the java.io.printStream Class (OUT)

CREATE A New StringBuffer (STR_BUF_1)

Duplicate The Stringbuffer

Call stringbuffer constructor

Store this stringbuffer in The Local Variable Array Position 1

Invoke the append method on str_buf_1 with str_1 as the argumentload the constant string "is my name." (STR_3)

Invoke the append method on str_buf_1 with str_3 as the argument

INVOKE TOSTRING METHOD ON STR_BUF_1 (STR_4)

INVOKE The Println Method on Out

This simple code creates five objects [Note 1]: STR_1, STR_2, STR_3, STR_4, AND STR_BUF_1.

It is necessary to pay attention to object creation relative to a very resource. Stack storage space must be assigned to all instance variables for each super class of each class and class; all instance variables must be initialized; and the constructor of the class and the constructor of each super class must be executed. In order to create an efficient code, it is limited to the creation of objects in absolutely necessary.

So, can I rewrite it with a more efficient method? Consider the following block:

StringBuffer name = new stringbuffer ("joe");

System.out.println ("IS my name."). TOSTRING ());

Please refer to the program list 2 for the corresponding byte code / pseudo code.

Listing 2: Byte code / pseudo code using StringBuffer Append operator

create new StringBuffer (STR_BUF_1) duplicate the StringBuffer load the constant String "Joe" (STR_1) call StringBuffer constructor store this StringBuffer in the local variable array position 1 get the static out field from the java.io.PrintStream class (OUT) load STR_BUF_1 Load the constant string "is my name." (STR_2) Invoke the append method on str_buf_1 with str_2 as the argument invoke toString Method on str_buf_1 (str_3) Invoke the Println Method on Out

The above code value creates four objects: STR_1, STR_2, STR_3, and STR_BUF_1. You may think that it is necessary to reduce the creation of an object. However, county cotton code created eight objects:

String name = new String ("joe");

Name = "is my";

Name = "Name."

And this code only creates 5:

StringBuffer name = new stringbuffer ("joe");

Name.Append ("is my");

Name.append ("name."). TOSTRING ();

The second paragraph code execution is more than twice the first paragraph [Note 2].

Conclusion: Use StringBuffer to improve the performance of string processing code. Its goal is to make the creation of new objects to minimize, which can be implemented by using an Append method on StringBuffer to use the connection operator on String.

Faster logging (Faster Logging) requires an appropriate logging mechanism in each software item I participated in the development. There are many reasons why the logging feature included in the application. The main reason is to make maintenance easier. In order to implement an error report in the released application, it is necessary to set the start point. In many cases, the report of the user is unclear, and the issues described may be caused by many factors. If there is an appropriate mechanism to collect additional information about this issue, then the problem that solves the problem is greatly reduced.

There is no standard approach to generate this information, usually, this is dependent on how developers have appropriately established this mechanism. However, the implementation of the logging mechanism has a large impact on the performance of the application. Our goal is to establish a mechanism for the establishment of a valuable runtime information but simultaneously to achieve a minimal impact on runtime performance.

The most obvious way to avoid runtime is not to include log records in the released application; in other words, if the actual code of the log record is not compiled into the application, then it will not affect performance. . Program Listing 3 shows a class that defines such a recording mechanism. It can be set to ignore the logging code from the generated byte code. This class will be a single element (Singleton) to avoid instances of unnecessary Logger classes.

Listing 3: You can configure the Logger class public class logger of a single element that does not produce a code in the published version.

// Example of this class

Private static logger thelogger = NULL;

// Debug Messages, which is allowed to debug messages when set to True, FALSE

Public Static Final Boolean CAN_Debug = True;

/ ** Private constructor - only one instance * /

Private logger () {}

/ ** Returns the only instance created by this class * /

Public static logger getInstance () {

IF (thelogger == null) {thelogger = new logger ();

Return thelogger;

}

Public void debugmsg (String MSG) {system.out.println (msg);

}

As you can see, this very simple class includes a type variable, a type constant, two methods, and a constructor. To use this class, just simply obtain its instance, check if Debug is started, and calls debugmsg, as shown below:

...

Logger mylogger = logger.getinstance ();

...

IF (logger.can_debug) {

MyLogger.debugmsg ("Some Debug Message");

}

Imagine Logger.can_Debug to false. When creating an application, the dead code will not have the byte code generated by this. This is because the compiler knows the final static variable logger.can_debug always False. If logger.can_debug is true, the code will be compiled and generated by the corresponding byte code. In this way, the compilation of the debug message will result in more byte code.

This approach can be extended to allowing more micro processing to the resulting information. For example, you can declare a new Static Final's Boolean variable CAN_INFO, and you can implement a new Public Void Infox (String MSG) method.

From the perspective of performance, this is the best way to use. Several different versions can collaborate to reflect the supported different levels of information. For example, you can post a product version and a debug version. If there is a problem in the product version, you can use the debug version to exchange it to identify the problem. The main disadvantage of this method is that it cannot be set at runtime, such as using it as a system attribute.

In most logging mechanisms, the main performance influencing factors are creation of String objects. In this way, our goal should be to make this overhead to the smallest. So the solution needs to include StringBuffer. The logger class in Listing 4 provides a configurable log recording level.

Listing 4: Provide configurable logging level Logger class public class logger {

...

// Controller of the message message

Public static final int CAN_INFO = 1;

// Debug message controller

Public static final int CAN_Debug = 2;

// Debug level - default as information message

Public int log_level = 1;

...

Public void setLoglevel (int level) {

IF (Level> = 0) {log_level = level;}

}

/ ** Returns true * / if the CAN_INFO bit is set

Public boolean caninfo () {

IF ((log_level & can_info) == can_info) {return true;}

Return False;

}

/ ** Returns true * / if the CAN_Debug bit is set

Public Boolean Candebug () {

IF ((log_level & can_debug) == can_debug) {return true;}

Return False;

}

Public void debugmsg (String MSG) {system.out.println (msg);

Public void infromsg (String MSG) {system.out.println (msg);

}

The above code example provides a two-stage logging method. It can handle debug messages and information messages, and it can easily extend to process more types. This class provides a solid foundation for logging mechanisms.

There are two options to use this implementation in the application. The first is to create a base class that implements a simple API, and the application will extends. The second is to implement an interface that defines a simple API is implemented. Below is an example of an interface:

Public interface logapi {

Public void createmsg ();

Public void appendlog (String Str);

Public void appendlog (int i);

Public void logdebugmsg ();

Public void loginfomsg ();

}

A implementation of this interface is provided in TestLogger.java (Program Listing 5).

Listing 5: Example TestLogger.java- define a simple API interface implementation / * Copyright (c) 2000 Stepping Stone Software Ltd, John Keyes * / public class TestLogger implements LogAPI {static Logger myLogger = Logger.getInstance (); StringBuffer msg = New stringbuffer (); public static void main (string args []) {string striew = system.getProperty ("app.loglevel"); if (strLlease! = null) {int level = integer.parseint (strolavel); MyLogger. SetLogger (); TestLogger Testlog = new testLogger (); testlog.test ();} testlogger () {} public void test () {} public void test () {Int Age = 24; string name = "joe"; if (MyLogger.candebug) )); Appendlog ("debug / n name:"); appendlog (name); appendlog ("age:"); appendlog (agn); logDebugmsg ();} if (myLogger.canInfo ()) { Createmsg (); appendlog ("info / n name:"); appendlog (name); appendlog ("age:"); appendlog (age); loginfomsg ();}} public void createmsg ()} TLENGTH (0); PUBLIC VOID APPENDLOG (String Str) {msg.Append (INT i) {msg.append (i);} public void logdebugmsg () {MYLogger.debugmsg (MSG. Tostring ());} public void loginfomsg () {mylogger.infomsg (msg.tostring ());}}

Reuse for StringBuffer objects is the key here. Usually you will write the following code as a debug message:

DEBUGMSG ("Name:" Name "Age:" AGE);

As discussed above, the creation of String types has an adverse effect on performance. If you rewrite as shown in TestLogger.java, the performance promotion will be apparent.

The level of logging can now be customized at runtime using the SetLoglevel method defined in the Logger class. Complete this work with the help of system properties is a good idea. You must define your own properties; in this example it is named "app.loglevel". If the problem discussed is an application, then you can use the -D switch to set the "app.loglevel" attribute [Note 3] for JVM. E.g:

Java -d app.loglevel = 3 MyApp On the other hand, if your program is an applet, you can use the tag:

So, in order to set the logging level, everything you have to do is to get the attribute value and call the result to setLoglevel method:

String string string = system.getproperty ("app.loglevel");

IF (strLlease! = Null) {

INT level = integer.parseint (strlevel);

Logger.getInstance (). SetLoglevel (Level);

}

The advantage of this method is:

l Reduce the object to create, that is, the object is reused

l A good API that can encourage all developers to comply with the same standard.

l Scandule - Separate developers can adjust this in advance to adapt to their own needs

l Reduce maintenance overhead by using standard APIs

l Logging levels customized at runtime

Better performance through custom set

When you need to store a series of normal objects, the easiest way is usually using java.util.Vector. This class is less efficient in many cases its use, and its low efficiency has two main reasons. The first reason is that the vector is a thread; therefore, many of its methods are synchronous. This synchronization causes unnecessary overhead when you know that the application is single-thread. The second reason for the VECTOR efficiency is the number of CASTINGs that are performed when the object is retrieved. If the object stored in the vector has the same type, it does not require type conversion. In this way, in order to achieve better performance, we need to be determined, single-threaded collection.

StringVector.java (Program List 6) is an example implementation of a string type collection. Remember this class available for all types of objects.

Listing 6: StringVector.java - String Type Collection Example Implementation / * Copyright (C) 2000 Stepping Stone Software Ltd, John Keyes * / Public Class StringVector {

PRIVATE;

Public stringvector () {this (10); // The default size is 10}

Public stringvector (int initialsize) {data = new string [initialsize];

Public void add (string str) {// ignore NULL string IF (str == null) {return;} ENSURECAPACITY (Count 1); Data [count ] = str;}

private void ensureCapacity (int minCapacity) {int oldCapacity = data.length; if (minCapacity> oldCapacity) {String oldData [] = data; int newCapacity = oldCapacity * 2; data = new String [newCapacity]; System.arraycopy (oldData, 0, DATA, 0, Count;}} public void remove (string str) {if (str == null) {return; // ignore NULL string} for (int i = 0; i

Public final string getStringat (int index) {if (index <0) {return null;} else if (index> count) {return null; // index is> # strings} else {return data [index]; // index IS good}} // NOT SHOWN: SIZE () TOSTRINGARRAY ()}

Previously you might have seen this code in some point:

...

Vector strings = new vector ();

Strings.Add ("one");

Strings.Add ("Two");

String second = (string) strings.ementat (1);

...

It can now be replaced with:

...

StringVector strings = new stringvector ();

Strings.Add ("one");

Strings.Add ("Two");

String second = strings.getstringat (1);

...

The result is improved performance. TestCollection.java (Listing 7) clearly reflects the difference in performance. The execution time of the ADD method of the StringVector class is only about 70% of the ADD method of the Vector class. The GetStringAT method has only 25% of the ELEMENTAT method of the VECTOR class.

Listing 7: TestCollection.java - significantly reflects performance of performance / * CopyRight (C) 2000 Stepping Stone Software Ltd, John Keyes * /

Import java.util.vector;

Public class testcollection {

Public static void main (string args []) {testcollection collect = new testcollection ();

IF (args.length == 0) {system.out.println ("usage: java testcollection"; system.exit (1);} if (args [0] .equals ("vector") ) {Vector store = new vector (); long start = system.currenttimemillis (); for (int i = 0; i <1000000; i ) {store.addelement ("string");} long finish = system.currentTimeMillis ); System.out.println (FINISH-START); start = system.currenttimemillis (); for (int i = 0; i <1000000; i ) {string result = (string) Store.Elementat (i); } Finish = system.currenttimemillis (); system.out.println;} else if (args [0] .Equals ("stringvector")) {stringVector store = new stringvector (); long start = System.currentTimeMillis (); for (int i = 0; i <1000000; i ) {store.add ("string");} long finish = system.currenttimemillis (); system.ou T.Println ((Finish-start); start = system.currenttimemillis (); for (int i = 0; i <1000000; i ) {string result = store.getstringat (i);} finish = system.currenttimemillis ); System.out.println (finish-start);}}}

This technique can be modified to adapt to the requirements you are engaged in applications. For example, you can create Intervertor, Employeevector, and more.

Conclude

This is not about the "Bible" of the Java program performance. Its purpose is to let you know that you can improve your performance by doing some minor changes to your Java code.

related resources

Towerj: http://www.towerj.com/

JROCKIT: http://www.jrockit.com/

Java virtual machine Description: http://java.sun.com/docs/books/vmspec

Comment

[1], such as http://java.sun.com/docs/books/vmspec/2nd-edition.htm/concepts.doc.htm#19124: Load a class or interface containing String constants may be created A new String object represents this constant. This article assumes that new String is created to represent constants.

[2] Example 3 Example 3 Examples of an average of 578 milliseconds, Example 4 executed 100,000 times average time 265 milliseconds (measured on my computer)

[3] -d switch is used by most JVM, Microsoft's JVIEW interpreter uses / d: Switch.

About author

John Keyes is senior developers of Stepping Stone Software Co., Ltd. He worked before Iona Technologies. You can contact him through JohnKeyes@yahoo.com.

转载请注明原文地址:https://www.9cbs.com/read-4320.html

New Post(0)